langchainrb 0.18.0 → 0.19.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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/README.md +4 -4
  4. data/lib/langchain/assistant/llm/adapter.rb +7 -6
  5. data/lib/langchain/assistant/llm/adapters/anthropic.rb +1 -3
  6. data/lib/langchain/assistant/llm/adapters/aws_bedrock_anthropic.rb +35 -0
  7. data/lib/langchain/assistant/llm/adapters/ollama.rb +1 -3
  8. data/lib/langchain/assistant/messages/anthropic_message.rb +89 -17
  9. data/lib/langchain/assistant/messages/base.rb +4 -0
  10. data/lib/langchain/assistant/messages/google_gemini_message.rb +62 -21
  11. data/lib/langchain/assistant/messages/mistral_ai_message.rb +69 -24
  12. data/lib/langchain/assistant/messages/ollama_message.rb +9 -5
  13. data/lib/langchain/assistant/messages/openai_message.rb +78 -26
  14. data/lib/langchain/assistant.rb +2 -1
  15. data/lib/langchain/llm/anthropic.rb +10 -10
  16. data/lib/langchain/llm/aws_bedrock.rb +75 -120
  17. data/lib/langchain/llm/azure.rb +1 -1
  18. data/lib/langchain/llm/base.rb +1 -1
  19. data/lib/langchain/llm/cohere.rb +8 -8
  20. data/lib/langchain/llm/google_gemini.rb +5 -6
  21. data/lib/langchain/llm/google_vertex_ai.rb +6 -5
  22. data/lib/langchain/llm/hugging_face.rb +4 -4
  23. data/lib/langchain/llm/mistral_ai.rb +4 -4
  24. data/lib/langchain/llm/ollama.rb +10 -8
  25. data/lib/langchain/llm/openai.rb +6 -5
  26. data/lib/langchain/llm/parameters/chat.rb +4 -1
  27. data/lib/langchain/llm/replicate.rb +6 -6
  28. data/lib/langchain/llm/response/ai21_response.rb +20 -0
  29. data/lib/langchain/tool_definition.rb +7 -0
  30. data/lib/langchain/utils/image_wrapper.rb +37 -0
  31. data/lib/langchain/version.rb +1 -1
  32. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f049086ef6cbb25483d360705c216583d30ca17a88e1a286730a00cbc95fa9fd
4
- data.tar.gz: '07592ccbf768d32122e98fc2cd79eb17547aa004591a9e0d9af3406bb782523d'
3
+ metadata.gz: 6c33b1ac19c1cf4a3a7de8fbf5d2568899d05662cb7db0b96321186c255ef312
4
+ data.tar.gz: 96bad2ff6f9b9a9cbac26699b525a8646482a0a77131d58fed84de3bafcb074e
5
5
  SHA512:
6
- metadata.gz: 32b1bd7ab1d86c3615ba1019476011031eb25429a2ff7d4ad0b3efb65ea04967fac668f9c92ea5a5f9e641ebf0b3bb638ec99a1883b6ef7ba8ddf807245c8ca4
7
- data.tar.gz: 67be74a35f8a4cc4a5ddd98f40f2e16d179f3127015ec8895f7764e0a1adafa2ac6a02c8493addd0468c04ee4c944cf08fbb3952512a87a05a5d368240d9d924
6
+ metadata.gz: aef30f32cfbdba307372ab8b20a0a484d22bd72f68d614056c6abb016b16dbd5e7a51dc15fb26746fd7a88a429d6282848aace42076c41e4fb31d58be9fb27f6
7
+ data.tar.gz: 3fd751a81f1121209ae2fac6bd25bb2e9d524b23dd5afe83967e10a78521e6994b7ca855e7e4343dded1117c5a7377d26cf15607f16247e9f220a27d17b91827
data/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
+ # CHANGELOG
2
+
3
+ ## Key
4
+ - [BREAKING]: A breaking change. After an upgrade, your app may need modifications to keep working correctly.
5
+ - [FEATURE]: A non-breaking improvement to the app. Either introduces new functionality, or improves on an existing feature.
6
+ - [BUGFIX]: Fixes a bug with a non-breaking change.
7
+ - [COMPAT]: Compatibility improvements - changes to make Langchain.rb more compatible with different dependency versions.
8
+ - [OPTIM]: Optimization or performance increase.
9
+ - [DOCS]: Documentation changes. No changes to the library's behavior.
10
+ - [SECURITY]: A change which fixes a security vulnerability.
11
+
1
12
  ## [Unreleased]
2
13
 
14
+ ## [0.19.1] - 2024-11-21
15
+ - [FEATURE] [https://github.com/patterns-ai-core/langchainrb/pull/858] Assistant, when using Anthropic, now also accepts image_url in the message.
16
+ - [FEATURE] [https://github.com/patterns-ai-core/langchainrb/pull/861] Clean up passing `max_tokens` to Anthropic constructor and chat method
17
+ - [FEATURE] [https://github.com/patterns-ai-core/langchainrb/pull/849] Langchain::Assistant now works with AWS Bedrock-hosted Anthropic models
18
+ - [OPTIM] [https://github.com/patterns-ai-core/langchainrb/pull/867] Refactor `GoogleGeminiMessage#to_hash` and `OpenAIMessage#to_hash` methods.
19
+ - [OPTIM] [https://github.com/patterns-ai-core/langchainrb/pull/849] Simplify Langchain::LLM::AwsBedrock class
20
+ - [BUGFIX] [https://github.com/patterns-ai-core/langchainrb/pull/869] AnthropicMessage now correctly handles tool calls with content.
21
+ - [OPTIM] [https://github.com/patterns-ai-core/langchainrb/pull/870] Assistant, when using Ollama (e.g.: llava model), now also accepts image_url in the message.
22
+
23
+ ## [0.19.0] - 2024-10-23
24
+ - [BREAKING] [https://github.com/patterns-ai-core/langchainrb/pull/840] Rename `chat_completion_model_name` parameter to `chat_model` in Langchain::LLM parameters.
25
+ - [BREAKING] [https://github.com/patterns-ai-core/langchainrb/pull/840] Rename `completion_model_name` parameter to `completion_model` in Langchain::LLM parameters.
26
+ - [BREAKING] [https://github.com/patterns-ai-core/langchainrb/pull/840] Rename `embeddings_model_name` parameter to `embedding_model` in Langchain::LLM parameters.
27
+ - [BUGFIX] [https://github.com/patterns-ai-core/langchainrb/pull/850/] Fix MistralAIMessage to handle "Tool" Output
28
+ - [BUGFIX] [https://github.com/patterns-ai-core/langchainrb/pull/837] Fix bug when tool functions with no input variables are used with Langchain::LLM::Anthropic
29
+ - [BUGFIX] [https://github.com/patterns-ai-core/langchainrb/pull/836] Fix bug when assistant.instructions = nil did not remove the system message
30
+ - [FEATURE] [https://github.com/patterns-ai-core/langchainrb/pull/838] Allow setting safety_settings: [] in default_options for Langchain::LLM::GoogleGemini and Langchain::LLM::GoogleVertexAI constructors
31
+ - [BUGFIX] [https://github.com/patterns-ai-core/langchainrb/pull/871] Allow passing in options hash to Ollama
32
+
3
33
  ## [0.18.0] - 2024-10-12
4
34
  - [BREAKING] Remove `Langchain::Assistant#clear_thread!` method
5
35
  - [BREAKING] `Langchain::Messages::*` namespace had migrated to `Langchain::Assistant::Messages::*`
data/README.md CHANGED
@@ -86,7 +86,7 @@ Most LLM classes can be initialized with an API key and optional default options
86
86
  ```ruby
87
87
  llm = Langchain::LLM::OpenAI.new(
88
88
  api_key: ENV["OPENAI_API_KEY"],
89
- default_options: { temperature: 0.7, chat_completion_model_name: "gpt-4o" }
89
+ default_options: { temperature: 0.7, chat_model: "gpt-4o" }
90
90
  )
91
91
  ```
92
92
 
@@ -133,7 +133,7 @@ messages = [
133
133
  { role: "system", content: "You are a helpful assistant." },
134
134
  { role: "user", content: "What's the weather like today?" }
135
135
  # Google Gemini and Google VertexAI expect messages in a different format:
136
- # { role: "user", parts: [{ text: "why is the sky blue?" }]
136
+ # { role: "user", parts: [{ text: "why is the sky blue?" }]}
137
137
  ]
138
138
  response = llm.chat(messages: messages)
139
139
  chat_completion = response.chat_completion
@@ -505,7 +505,7 @@ assistant.add_message_and_run!(content: "What's the latest news about AI?")
505
505
  # Supply an image to the assistant
506
506
  assistant.add_message_and_run!(
507
507
  content: "Show me a picture of a cat",
508
- image: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
508
+ image_url: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
509
509
  )
510
510
 
511
511
  # Access the conversation thread
@@ -558,7 +558,7 @@ Note that streaming is not currently supported for all LLMs.
558
558
  The Langchain::Assistant can be easily extended with custom tools by creating classes that `extend Langchain::ToolDefinition` module and implement required methods.
559
559
  ```ruby
560
560
  class MovieInfoTool
561
- include Langchain::ToolDefinition
561
+ extend Langchain::ToolDefinition
562
562
 
563
563
  define_function :search_movie, description: "MovieInfoTool: Search for a movie by title" do
564
564
  property :query, type: "string", description: "The movie title to search for", required: true
@@ -6,16 +6,17 @@ module Langchain
6
6
  # TODO: Fix the message truncation when context window is exceeded
7
7
  class Adapter
8
8
  def self.build(llm)
9
- case llm
10
- when Langchain::LLM::Anthropic
9
+ if llm.is_a?(Langchain::LLM::Anthropic)
11
10
  LLM::Adapters::Anthropic.new
12
- when Langchain::LLM::GoogleGemini, Langchain::LLM::GoogleVertexAI
11
+ elsif llm.is_a?(Langchain::LLM::AwsBedrock) && llm.defaults[:chat_model].include?("anthropic")
12
+ LLM::Adapters::AwsBedrockAnthropic.new
13
+ elsif llm.is_a?(Langchain::LLM::GoogleGemini) || llm.is_a?(Langchain::LLM::GoogleVertexAI)
13
14
  LLM::Adapters::GoogleGemini.new
14
- when Langchain::LLM::MistralAI
15
+ elsif llm.is_a?(Langchain::LLM::MistralAI)
15
16
  LLM::Adapters::MistralAI.new
16
- when Langchain::LLM::Ollama
17
+ elsif llm.is_a?(Langchain::LLM::Ollama)
17
18
  LLM::Adapters::Ollama.new
18
- when Langchain::LLM::OpenAI
19
+ elsif llm.is_a?(Langchain::LLM::OpenAI)
19
20
  LLM::Adapters::OpenAI.new
20
21
  else
21
22
  raise ArgumentError, "Unsupported LLM type: #{llm.class}"
@@ -38,9 +38,7 @@ module Langchain
38
38
  # @param tool_call_id [String] The tool call ID
39
39
  # @return [Messages::AnthropicMessage] The Anthropic message
40
40
  def build_message(role:, content: nil, image_url: nil, tool_calls: [], tool_call_id: nil)
41
- Langchain.logger.warn "WARNING: Image URL is not supported by Anthropic currently" if image_url
42
-
43
- Messages::AnthropicMessage.new(role: role, content: content, tool_calls: tool_calls, tool_call_id: tool_call_id)
41
+ Messages::AnthropicMessage.new(role: role, content: content, image_url: image_url, tool_calls: tool_calls, tool_call_id: tool_call_id)
44
42
  end
45
43
 
46
44
  # Extract the tool call information from the Anthropic tool call hash
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Langchain
4
+ class Assistant
5
+ module LLM
6
+ module Adapters
7
+ class AwsBedrockAnthropic < Anthropic
8
+ private
9
+
10
+ # @param [String] choice
11
+ # @param [Boolean] _parallel_tool_calls
12
+ # @return [Hash]
13
+ def build_tool_choice(choice, _parallel_tool_calls)
14
+ # Aws Bedrock hosted Anthropic does not support parallel tool calls
15
+ Langchain.logger.warn "WARNING: parallel_tool_calls is not supported by AWS Bedrock Anthropic currently"
16
+
17
+ tool_choice_object = {}
18
+
19
+ case choice
20
+ when "auto"
21
+ tool_choice_object[:type] = "auto"
22
+ when "any"
23
+ tool_choice_object[:type] = "any"
24
+ else
25
+ tool_choice_object[:type] = "tool"
26
+ tool_choice_object[:name] = choice
27
+ end
28
+
29
+ tool_choice_object
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -39,9 +39,7 @@ module Langchain
39
39
  # @param tool_call_id [String] The tool call ID
40
40
  # @return [Messages::OllamaMessage] The Ollama message
41
41
  def build_message(role:, content: nil, image_url: nil, tool_calls: [], tool_call_id: nil)
42
- Langchain.logger.warn "WARNING: Image URL is not supported by Ollama currently" if image_url
43
-
44
- Messages::OllamaMessage.new(role: role, content: content, tool_calls: tool_calls, tool_call_id: tool_call_id)
42
+ Messages::OllamaMessage.new(role: role, content: content, image_url: image_url, tool_calls: tool_calls, tool_call_id: tool_call_id)
45
43
  end
46
44
 
47
45
  # Extract the tool call information from the OpenAI tool call hash
@@ -12,13 +12,26 @@ module Langchain
12
12
 
13
13
  TOOL_ROLE = "tool_result"
14
14
 
15
- def initialize(role:, content: nil, tool_calls: [], tool_call_id: nil)
15
+ # Initialize a new Anthropic message
16
+ #
17
+ # @param role [String] The role of the message
18
+ # @param content [String] The content of the message
19
+ # @param tool_calls [Array<Hash>] The tool calls made in the message
20
+ # @param tool_call_id [String] The ID of the tool call
21
+ def initialize(
22
+ role:,
23
+ content: nil,
24
+ image_url: nil,
25
+ tool_calls: [],
26
+ tool_call_id: nil
27
+ )
16
28
  raise ArgumentError, "Role must be one of #{ROLES.join(", ")}" unless ROLES.include?(role)
17
29
  raise ArgumentError, "Tool calls must be an array of hashes" unless tool_calls.is_a?(Array) && tool_calls.all? { |tool_call| tool_call.is_a?(Hash) }
18
30
 
19
31
  @role = role
20
32
  # Some Tools return content as a JSON hence `.to_s`
21
33
  @content = content.to_s
34
+ @image_url = image_url
22
35
  @tool_calls = tool_calls
23
36
  @tool_call_id = tool_call_id
24
37
  end
@@ -27,23 +40,82 @@ module Langchain
27
40
  #
28
41
  # @return [Hash] The message as an Anthropic API-compatible hash
29
42
  def to_hash
30
- {}.tap do |h|
31
- h[:role] = tool? ? "user" : role
32
-
33
- h[:content] = if tool?
34
- [
35
- {
36
- type: "tool_result",
37
- tool_use_id: tool_call_id,
38
- content: content
39
- }
40
- ]
41
- elsif tool_calls.any?
42
- tool_calls
43
- else
44
- content
45
- end
43
+ if assistant?
44
+ assistant_hash
45
+ elsif tool?
46
+ tool_hash
47
+ elsif user?
48
+ user_hash
49
+ end
50
+ end
51
+
52
+ # Convert the message to an Anthropic API-compatible hash
53
+ #
54
+ # @return [Hash] The message as an Anthropic API-compatible hash, with the role as "assistant"
55
+ def assistant_hash
56
+ {
57
+ role: "assistant",
58
+ content: [
59
+ {
60
+ type: "text",
61
+ text: content
62
+ }
63
+ ].concat(tool_calls)
64
+ }
65
+ end
66
+
67
+ # Convert the message to an Anthropic API-compatible hash
68
+ #
69
+ # @return [Hash] The message as an Anthropic API-compatible hash, with the role as "user"
70
+ def tool_hash
71
+ {
72
+ role: "user",
73
+ # TODO: Tool can also return images
74
+ # https://docs.anthropic.com/en/docs/build-with-claude/tool-use#handling-tool-use-and-tool-result-content-blocks
75
+ content: [
76
+ {
77
+ type: "tool_result",
78
+ tool_use_id: tool_call_id,
79
+ content: content
80
+ }
81
+ ]
82
+ }
83
+ end
84
+
85
+ # Convert the message to an Anthropic API-compatible hash
86
+ #
87
+ # @return [Hash] The message as an Anthropic API-compatible hash, with the role as "user"
88
+ def user_hash
89
+ {
90
+ role: "user",
91
+ content: build_content_array
92
+ }
93
+ end
94
+
95
+ # Builds the content value for the message hash
96
+ # @return [Array<Hash>] An array of content hashes
97
+ def build_content_array
98
+ content_details = []
99
+
100
+ if content && !content.empty?
101
+ content_details << {
102
+ type: "text",
103
+ text: content
104
+ }
46
105
  end
106
+
107
+ if image
108
+ content_details << {
109
+ type: "image",
110
+ source: {
111
+ type: "base64",
112
+ data: image.base64,
113
+ media_type: image.mime_type
114
+ }
115
+ }
116
+ end
117
+
118
+ content_details
47
119
  end
48
120
 
49
121
  # Check if the message is a tool call
@@ -50,6 +50,10 @@ module Langchain
50
50
  # TODO: Should we return :unknown or raise an error?
51
51
  :unknown
52
52
  end
53
+
54
+ def image
55
+ image_url ? Utils::ImageWrapper.new(image_url) : nil
56
+ end
53
57
  end
54
58
  end
55
59
  end
@@ -15,10 +15,10 @@ module Langchain
15
15
 
16
16
  # Initialize a new Google Gemini message
17
17
  #
18
- # @param [String] The role of the message
19
- # @param [String] The content of the message
20
- # @param [Array<Hash>] The tool calls made in the message
21
- # @param [String] The ID of the tool call
18
+ # @param role [String] The role of the message
19
+ # @param content [String] The content of the message
20
+ # @param tool_calls [Array<Hash>] The tool calls made in the message
21
+ # @param tool_call_id [String] The ID of the tool call
22
22
  def initialize(role:, content: nil, tool_calls: [], tool_call_id: nil)
23
23
  raise ArgumentError, "Role must be one of #{ROLES.join(", ")}" unless ROLES.include?(role)
24
24
  raise ArgumentError, "Tool calls must be an array of hashes" unless tool_calls.is_a?(Array) && tool_calls.all? { |tool_call| tool_call.is_a?(Hash) }
@@ -41,23 +41,12 @@ module Langchain
41
41
  #
42
42
  # @return [Hash] The message as a Google Gemini API-compatible hash
43
43
  def to_hash
44
- {}.tap do |h|
45
- h[:role] = role
46
- h[:parts] = if function?
47
- [{
48
- functionResponse: {
49
- name: tool_call_id,
50
- response: {
51
- name: tool_call_id,
52
- content: content
53
- }
54
- }
55
- }]
56
- elsif tool_calls.any?
57
- tool_calls
58
- else
59
- [{text: content}]
60
- end
44
+ if tool?
45
+ tool_hash
46
+ elsif model?
47
+ model_hash
48
+ elsif user?
49
+ user_hash
61
50
  end
62
51
  end
63
52
 
@@ -73,6 +62,13 @@ module Langchain
73
62
  function?
74
63
  end
75
64
 
65
+ # Check if the message is a user call
66
+ #
67
+ # @return [Boolean] true/false whether this message is a user call
68
+ def user?
69
+ role == "user"
70
+ end
71
+
76
72
  # Check if the message is a tool call
77
73
  #
78
74
  # @return [Boolean] true/false whether this message is a tool call
@@ -80,6 +76,51 @@ module Langchain
80
76
  role == "function"
81
77
  end
82
78
 
79
+ # Convert the message to an GoogleGemini API-compatible hash
80
+ # @return [Hash] The message as an GoogleGemini API-compatible hash, with the role as "model"
81
+ def model_hash
82
+ {
83
+ role: role,
84
+ parts: build_parts
85
+ }
86
+ end
87
+
88
+ # Convert the message to an GoogleGemini API-compatible hash
89
+ # @return [Hash] The message as an GoogleGemini API-compatible hash, with the role as "function"
90
+ def tool_hash
91
+ {
92
+ role: role,
93
+ parts: [{
94
+ functionResponse: {
95
+ name: tool_call_id,
96
+ response: {
97
+ name: tool_call_id,
98
+ content: content
99
+ }
100
+ }
101
+ }]
102
+ }
103
+ end
104
+
105
+ # Convert the message to an GoogleGemini API-compatible hash
106
+ # @return [Hash] The message as an GoogleGemini API-compatible hash, with the role as "user"
107
+ def user_hash
108
+ {
109
+ role: role,
110
+ parts: build_parts
111
+ }
112
+ end
113
+
114
+ # Builds the part value for the message hash
115
+ # @return [Array<Hash>] An array of content hashes of the text or of the tool calls if present
116
+ def build_parts
117
+ if tool_calls.any?
118
+ tool_calls
119
+ else
120
+ [{text: content}]
121
+ end
122
+ end
123
+
83
124
  # Check if the message came from an LLM
84
125
  #
85
126
  # @return [Boolean] true/false whether this message was produced by an LLM
@@ -45,30 +45,14 @@ module Langchain
45
45
  #
46
46
  # @return [Hash] The message as an MistralAI API-compatible hash
47
47
  def to_hash
48
- {}.tap do |h|
49
- h[:role] = role
50
-
51
- if tool_calls.any?
52
- h[:tool_calls] = tool_calls
53
- else
54
- h[:tool_call_id] = tool_call_id if tool_call_id
55
-
56
- h[:content] = []
57
-
58
- if content && !content.empty?
59
- h[:content] << {
60
- type: "text",
61
- text: content
62
- }
63
- end
64
-
65
- if image_url
66
- h[:content] << {
67
- type: "image_url",
68
- image_url: image_url
69
- }
70
- end
71
- end
48
+ if assistant?
49
+ assistant_hash
50
+ elsif system?
51
+ system_hash
52
+ elsif tool?
53
+ tool_hash
54
+ elsif user?
55
+ user_hash
72
56
  end
73
57
  end
74
58
 
@@ -92,6 +76,67 @@ module Langchain
92
76
  def tool?
93
77
  role == "tool"
94
78
  end
79
+
80
+ # Convert the message to an MistralAI API-compatible hash
81
+ # @return [Hash] The message as an MistralAI API-compatible hash, with the role as "assistant"
82
+ def assistant_hash
83
+ {
84
+ role: "assistant",
85
+ content: content,
86
+ tool_calls: tool_calls,
87
+ prefix: false
88
+ }
89
+ end
90
+
91
+ # Convert the message to an MistralAI API-compatible hash
92
+ # @return [Hash] The message as an MistralAI API-compatible hash, with the role as "system"
93
+ def system_hash
94
+ {
95
+ role: "system",
96
+ content: build_content_array
97
+ }
98
+ end
99
+
100
+ # Convert the message to an MistralAI API-compatible hash
101
+ # @return [Hash] The message as an MistralAI API-compatible hash, with the role as "tool"
102
+ def tool_hash
103
+ {
104
+ role: "tool",
105
+ content: content,
106
+ tool_call_id: tool_call_id
107
+ }
108
+ end
109
+
110
+ # Convert the message to an MistralAI API-compatible hash
111
+ # @return [Hash] The message as an MistralAI API-compatible hash, with the role as "user"
112
+ def user_hash
113
+ {
114
+ role: "user",
115
+ content: build_content_array
116
+ }
117
+ end
118
+
119
+ # Builds the content value for the message hash
120
+ # @return [Array<Hash>] An array of content hashes, with keys :type and :text or :image_url.
121
+ def build_content_array
122
+ content_details = []
123
+
124
+ if content && !content.empty?
125
+ content_details << {
126
+ type: "text",
127
+ text: content
128
+ }
129
+ end
130
+
131
+ if image_url
132
+ content_details << {
133
+ type: "image_url",
134
+ image_url: image_url
135
+ }
136
+ end
137
+
138
+ content_details
139
+ end
95
140
  end
96
141
  end
97
142
  end
@@ -16,17 +16,20 @@ module Langchain
16
16
 
17
17
  # Initialize a new OpenAI message
18
18
  #
19
- # @param [String] The role of the message
20
- # @param [String] The content of the message
21
- # @param [Array<Hash>] The tool calls made in the message
22
- # @param [String] The ID of the tool call
23
- def initialize(role:, content: nil, tool_calls: [], tool_call_id: nil)
19
+ # @param role [String] The role of the message
20
+ # @param content [String] The content of the message
21
+ # @param image_url [String] The URL of the image to include in the message
22
+ # @param tool_calls [Array<Hash>] The tool calls made in the message
23
+ # @param tool_call_id [String] The ID of the tool call
24
+ def initialize(role:, content: nil, image_url: nil, tool_calls: [], tool_call_id: nil)
24
25
  raise ArgumentError, "Role must be one of #{ROLES.join(", ")}" unless ROLES.include?(role)
25
26
  raise ArgumentError, "Tool calls must be an array of hashes" unless tool_calls.is_a?(Array) && tool_calls.all? { |tool_call| tool_call.is_a?(Hash) }
27
+ raise ArgumentError, "image_url must be a valid url" if image_url && !URI::DEFAULT_PARSER.make_regexp.match?(image_url)
26
28
 
27
29
  @role = role
28
30
  # Some Tools return content as a JSON hence `.to_s`
29
31
  @content = content.to_s
32
+ @image_url = image_url
30
33
  @tool_calls = tool_calls
31
34
  @tool_call_id = tool_call_id
32
35
  end
@@ -38,6 +41,7 @@ module Langchain
38
41
  {}.tap do |h|
39
42
  h[:role] = role
40
43
  h[:content] = content if content # Content is nil for tool calls
44
+ h[:images] = [image.base64] if image
41
45
  h[:tool_calls] = tool_calls if tool_calls.any?
42
46
  h[:tool_call_id] = tool_call_id if tool_call_id
43
47
  end