langchainrb 0.18.0 → 0.19.1

Sign up to get free protection for your applications and to get access to all the features.
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