langchainrb 0.8.1 → 0.9.0

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +53 -25
  4. data/lib/langchain/assistants/assistant.rb +199 -0
  5. data/lib/langchain/assistants/message.rb +58 -0
  6. data/lib/langchain/assistants/thread.rb +34 -0
  7. data/lib/langchain/chunker/markdown.rb +39 -0
  8. data/lib/langchain/conversation/memory.rb +1 -6
  9. data/lib/langchain/conversation.rb +7 -18
  10. data/lib/langchain/data.rb +4 -3
  11. data/lib/langchain/llm/ai21.rb +1 -1
  12. data/lib/langchain/llm/azure.rb +10 -97
  13. data/lib/langchain/llm/base.rb +1 -0
  14. data/lib/langchain/llm/cohere.rb +4 -6
  15. data/lib/langchain/llm/google_palm.rb +2 -0
  16. data/lib/langchain/llm/google_vertex_ai.rb +12 -10
  17. data/lib/langchain/llm/openai.rb +104 -160
  18. data/lib/langchain/llm/replicate.rb +0 -6
  19. data/lib/langchain/llm/response/anthropic_response.rb +4 -0
  20. data/lib/langchain/llm/response/google_palm_response.rb +4 -0
  21. data/lib/langchain/llm/response/ollama_response.rb +5 -1
  22. data/lib/langchain/llm/response/openai_response.rb +8 -0
  23. data/lib/langchain/loader.rb +3 -2
  24. data/lib/langchain/processors/markdown.rb +17 -0
  25. data/lib/langchain/tool/base.rb +24 -0
  26. data/lib/langchain/tool/google_search.rb +1 -4
  27. data/lib/langchain/utils/token_length/ai21_validator.rb +6 -2
  28. data/lib/langchain/utils/token_length/base_validator.rb +1 -1
  29. data/lib/langchain/utils/token_length/cohere_validator.rb +6 -2
  30. data/lib/langchain/utils/token_length/google_palm_validator.rb +5 -1
  31. data/lib/langchain/utils/token_length/openai_validator.rb +41 -0
  32. data/lib/langchain/vectorsearch/base.rb +2 -2
  33. data/lib/langchain/vectorsearch/epsilla.rb +5 -1
  34. data/lib/langchain/vectorsearch/pinecone.rb +2 -2
  35. data/lib/langchain/version.rb +1 -1
  36. data/lib/langchain.rb +2 -1
  37. metadata +10 -5
@@ -4,7 +4,7 @@ module Langchain::LLM
4
4
  # LLM interface for Azure OpenAI Service APIs: https://learn.microsoft.com/en-us/azure/ai-services/openai/
5
5
  #
6
6
  # Gem requirements:
7
- # gem "ruby-openai", "~> 6.1.0"
7
+ # gem "ruby-openai", "~> 6.3.0"
8
8
  #
9
9
  # Usage:
10
10
  # openai = Langchain::LLM::Azure.new(api_key:, llm_options: {}, embedding_deployment_url: chat_deployment_url:)
@@ -34,106 +34,19 @@ module Langchain::LLM
34
34
  @defaults = DEFAULTS.merge(default_options)
35
35
  end
36
36
 
37
- #
38
- # Generate an embedding for a given text
39
- #
40
- # @param text [String] The text to generate an embedding for
41
- # @param params extra parameters passed to OpenAI::Client#embeddings
42
- # @return [Langchain::LLM::OpenAIResponse] Response object
43
- #
44
- def embed(text:, **params)
45
- parameters = {model: @defaults[:embeddings_model_name], input: text}
46
-
47
- validate_max_tokens(text, parameters[:model])
48
-
49
- response = with_api_error_handling do
50
- embed_client.embeddings(parameters: parameters.merge(params))
51
- end
52
-
53
- Langchain::LLM::OpenAIResponse.new(response)
37
+ def embed(...)
38
+ @client = @embed_client
39
+ super(...)
54
40
  end
55
41
 
56
- #
57
- # Generate a completion for a given prompt
58
- #
59
- # @param prompt [String] The prompt to generate a completion for
60
- # @param params extra parameters passed to OpenAI::Client#complete
61
- # @return [Langchain::LLM::Response::OpenaAI] Response object
62
- #
63
- def complete(prompt:, **params)
64
- parameters = compose_parameters @defaults[:completion_model_name], params
65
-
66
- parameters[:messages] = compose_chat_messages(prompt: prompt)
67
- parameters[:max_tokens] = validate_max_tokens(parameters[:messages], parameters[:model])
68
-
69
- response = with_api_error_handling do
70
- chat_client.chat(parameters: parameters)
71
- end
72
-
73
- Langchain::LLM::OpenAIResponse.new(response)
42
+ def complete(...)
43
+ @client = @chat_client
44
+ super(...)
74
45
  end
75
46
 
76
- #
77
- # Generate a chat completion for a given prompt or messages.
78
- #
79
- # == Examples
80
- #
81
- # # simplest case, just give a prompt
82
- # openai.chat prompt: "When was Ruby first released?"
83
- #
84
- # # prompt plus some context about how to respond
85
- # openai.chat context: "You are RubyGPT, a helpful chat bot for helping people learn Ruby", prompt: "Does Ruby have a REPL like IPython?"
86
- #
87
- # # full control over messages that get sent, equivilent to the above
88
- # openai.chat messages: [
89
- # {
90
- # role: "system",
91
- # content: "You are RubyGPT, a helpful chat bot for helping people learn Ruby", prompt: "Does Ruby have a REPL like IPython?"
92
- # },
93
- # {
94
- # role: "user",
95
- # content: "When was Ruby first released?"
96
- # }
97
- # ]
98
- #
99
- # # few-short prompting with examples
100
- # openai.chat prompt: "When was factory_bot released?",
101
- # examples: [
102
- # {
103
- # role: "user",
104
- # content: "When was Ruby on Rails released?"
105
- # }
106
- # {
107
- # role: "assistant",
108
- # content: "2004"
109
- # },
110
- # ]
111
- #
112
- # @param prompt [String] The prompt to generate a chat completion for
113
- # @param messages [Array<Hash>] The messages that have been sent in the conversation
114
- # @param context [String] An initial context to provide as a system message, ie "You are RubyGPT, a helpful chat bot for helping people learn Ruby"
115
- # @param examples [Array<Hash>] Examples of messages to provide to the model. Useful for Few-Shot Prompting
116
- # @param options [Hash] extra parameters passed to OpenAI::Client#chat
117
- # @yield [Hash] Stream responses back one token at a time
118
- # @return [Langchain::LLM::OpenAIResponse] Response object
119
- #
120
- def chat(prompt: "", messages: [], context: "", examples: [], **options, &block)
121
- raise ArgumentError.new(":prompt or :messages argument is expected") if prompt.empty? && messages.empty?
122
-
123
- parameters = compose_parameters @defaults[:chat_completion_model_name], options, &block
124
- parameters[:messages] = compose_chat_messages(prompt: prompt, messages: messages, context: context, examples: examples)
125
-
126
- if functions
127
- parameters[:functions] = functions
128
- else
129
- parameters[:max_tokens] = validate_max_tokens(parameters[:messages], parameters[:model])
130
- end
131
-
132
- response = with_api_error_handling { chat_client.chat(parameters: parameters) }
133
-
134
- return if block
135
-
136
- Langchain::LLM::OpenAIResponse.new(response)
47
+ def chat(...)
48
+ @client = @chat_client
49
+ super(...)
137
50
  end
138
51
  end
139
52
  end
@@ -11,6 +11,7 @@ module Langchain::LLM
11
11
  # - {Langchain::LLM::Azure}
12
12
  # - {Langchain::LLM::Cohere}
13
13
  # - {Langchain::LLM::GooglePalm}
14
+ # - {Langchain::LLM::GoogleVertexAi}
14
15
  # - {Langchain::LLM::HuggingFace}
15
16
  # - {Langchain::LLM::LlamaCpp}
16
17
  # - {Langchain::LLM::OpenAI}
@@ -62,17 +62,15 @@ module Langchain::LLM
62
62
 
63
63
  default_params.merge!(params)
64
64
 
65
- default_params[:max_tokens] = Langchain::Utils::TokenLength::CohereValidator.validate_max_tokens!(prompt, default_params[:model], client)
65
+ default_params[:max_tokens] = Langchain::Utils::TokenLength::CohereValidator.validate_max_tokens!(prompt, default_params[:model], llm: client)
66
66
 
67
67
  response = client.generate(**default_params)
68
68
  Langchain::LLM::CohereResponse.new response, model: @defaults[:completion_model_name]
69
69
  end
70
70
 
71
- # Cohere does not have a dedicated chat endpoint, so instead we call `complete()`
72
- def chat(...)
73
- response_text = complete(...)
74
- ::Langchain::Conversation::Response.new(response_text)
75
- end
71
+ # TODO: Implement chat method: https://github.com/andreibondarev/cohere-ruby/issues/11
72
+ # def chat
73
+ # end
76
74
 
77
75
  # Generate a summary in English for a given text
78
76
  #
@@ -23,6 +23,8 @@ module Langchain::LLM
23
23
  "assistant" => "ai"
24
24
  }
25
25
 
26
+ attr_reader :defaults
27
+
26
28
  def initialize(api_key:, default_options: {})
27
29
  depends_on "google_palm_api"
28
30
 
@@ -21,6 +21,9 @@ module Langchain::LLM
21
21
  embeddings_model_name: "textembedding-gecko"
22
22
  }.freeze
23
23
 
24
+ # TODO: Implement token length validation
25
+ # LENGTH_VALIDATOR = Langchain::Utils::TokenLength::...
26
+
24
27
  # Google Cloud has a project id and a specific region of deployment.
25
28
  # For GenAI-related things, a safe choice is us-central1.
26
29
  attr_reader :project_id, :client, :region
@@ -135,15 +138,14 @@ module Langchain::LLM
135
138
  )
136
139
  end
137
140
 
138
- def chat(...)
139
- # https://cloud.google.com/vertex-ai/docs/samples/aiplatform-sdk-chathat
140
- # Chat params: https://cloud.google.com/vertex-ai/docs/samples/aiplatform-sdk-chat
141
- # \"temperature\": 0.3,\n"
142
- # + " \"maxDecodeSteps\": 200,\n"
143
- # + " \"topP\": 0.8,\n"
144
- # + " \"topK\": 40\n"
145
- # + "}";
146
- raise NotImplementedError, "coming soon for Vertex AI.."
147
- end
141
+ # def chat(...)
142
+ # https://cloud.google.com/vertex-ai/docs/samples/aiplatform-sdk-chathat
143
+ # Chat params: https://cloud.google.com/vertex-ai/docs/samples/aiplatform-sdk-chat
144
+ # \"temperature\": 0.3,\n"
145
+ # + " \"maxDecodeSteps\": 200,\n"
146
+ # + " \"topP\": 0.8,\n"
147
+ # + " \"topK\": 40\n"
148
+ # + "}";
149
+ # end
148
150
  end
149
151
  end
@@ -4,156 +4,170 @@ module Langchain::LLM
4
4
  # LLM interface for OpenAI APIs: https://platform.openai.com/overview
5
5
  #
6
6
  # Gem requirements:
7
- # gem "ruby-openai", "~> 6.1.0"
7
+ # gem "ruby-openai", "~> 6.3.0"
8
8
  #
9
9
  # Usage:
10
- # openai = Langchain::LLM::OpenAI.new(api_key:, llm_options: {})
11
- #
10
+ # openai = Langchain::LLM::OpenAI.new(
11
+ # api_key: ENV["OPENAI_API_KEY"],
12
+ # llm_options: {},
13
+ # default_options: {}
14
+ # )
12
15
  class OpenAI < Base
13
16
  DEFAULTS = {
14
17
  n: 1,
15
18
  temperature: 0.0,
16
- completion_model_name: "gpt-3.5-turbo",
17
19
  chat_completion_model_name: "gpt-3.5-turbo",
18
20
  embeddings_model_name: "text-embedding-ada-002",
19
21
  dimension: 1536
20
22
  }.freeze
21
23
 
22
- LEGACY_COMPLETION_MODELS = %w[
23
- ada
24
- babbage
25
- curie
26
- davinci
27
- ].freeze
28
-
29
24
  LENGTH_VALIDATOR = Langchain::Utils::TokenLength::OpenAIValidator
30
25
 
31
- attr_accessor :functions
26
+ attr_reader :defaults
32
27
 
28
+ # Initialize an OpenAI LLM instance
29
+ #
30
+ # @param api_key [String] The API key to use
31
+ # @param client_options [Hash] Options to pass to the OpenAI::Client constructor
33
32
  def initialize(api_key:, llm_options: {}, default_options: {})
34
33
  depends_on "ruby-openai", req: "openai"
35
34
 
36
35
  @client = ::OpenAI::Client.new(access_token: api_key, **llm_options)
36
+
37
37
  @defaults = DEFAULTS.merge(default_options)
38
38
  end
39
39
 
40
- #
41
40
  # Generate an embedding for a given text
42
41
  #
43
42
  # @param text [String] The text to generate an embedding for
44
- # @param params extra parameters passed to OpenAI::Client#embeddings
43
+ # @param model [String] ID of the model to use
44
+ # @param encoding_format [String] The format to return the embeddings in. Can be either float or base64.
45
+ # @param user [String] A unique identifier representing your end-user
45
46
  # @return [Langchain::LLM::OpenAIResponse] Response object
46
- #
47
- def embed(text:, **params)
48
- parameters = {model: @defaults[:embeddings_model_name], input: text}
47
+ def embed(
48
+ text:,
49
+ model: defaults[:embeddings_model_name],
50
+ encoding_format: nil,
51
+ user: nil
52
+ )
53
+ raise ArgumentError.new("text argument is required") if text.empty?
54
+ raise ArgumentError.new("model argument is required") if model.empty?
55
+ raise ArgumentError.new("encoding_format must be either float or base64") if encoding_format && %w[float base64].include?(encoding_format)
56
+
57
+ parameters = {
58
+ input: text,
59
+ model: model
60
+ }
61
+ parameters[:encoding_format] = encoding_format if encoding_format
62
+ parameters[:user] = user if user
49
63
 
50
64
  validate_max_tokens(text, parameters[:model])
51
65
 
52
66
  response = with_api_error_handling do
53
- client.embeddings(parameters: parameters.merge(params))
67
+ client.embeddings(parameters: parameters)
54
68
  end
55
69
 
56
70
  Langchain::LLM::OpenAIResponse.new(response)
57
71
  end
58
72
 
59
- #
73
+ # rubocop:disable Style/ArgumentsForwarding
60
74
  # Generate a completion for a given prompt
61
75
  #
62
76
  # @param prompt [String] The prompt to generate a completion for
63
- # @param params extra parameters passed to OpenAI::Client#complete
64
- # @return [Langchain::LLM::Response::OpenaAI] Response object
65
- #
77
+ # @param params [Hash] The parameters to pass to the `chat()` method
78
+ # @return [Langchain::LLM::OpenAIResponse] Response object
66
79
  def complete(prompt:, **params)
67
- parameters = compose_parameters @defaults[:completion_model_name], params
68
-
69
- return legacy_complete(prompt, parameters) if is_legacy_model?(parameters[:model])
70
-
71
- parameters[:messages] = compose_chat_messages(prompt: prompt)
72
- parameters[:max_tokens] = validate_max_tokens(parameters[:messages], parameters[:model], parameters[:max_tokens])
73
-
74
- response = with_api_error_handling do
75
- client.chat(parameters: parameters)
80
+ if params[:stop_sequences]
81
+ params[:stop] = params.delete(:stop_sequences)
76
82
  end
77
-
78
- Langchain::LLM::OpenAIResponse.new(response)
83
+ # Should we still accept the `messages: []` parameter here?
84
+ messages = [{role: "user", content: prompt}]
85
+ chat(messages: messages, **params)
79
86
  end
87
+ # rubocop:enable Style/ArgumentsForwarding
80
88
 
81
- #
82
89
  # Generate a chat completion for a given prompt or messages.
83
90
  #
84
- # == Examples
85
- #
86
- # # simplest case, just give a prompt
87
- # openai.chat prompt: "When was Ruby first released?"
88
- #
89
- # # prompt plus some context about how to respond
90
- # openai.chat context: "You are RubyGPT, a helpful chat bot for helping people learn Ruby", prompt: "Does Ruby have a REPL like IPython?"
91
- #
92
- # # full control over messages that get sent, equivilent to the above
93
- # openai.chat messages: [
94
- # {
95
- # role: "system",
96
- # content: "You are RubyGPT, a helpful chat bot for helping people learn Ruby", prompt: "Does Ruby have a REPL like IPython?"
97
- # },
98
- # {
99
- # role: "user",
100
- # content: "When was Ruby first released?"
101
- # }
102
- # ]
103
- #
104
- # # few-short prompting with examples
105
- # openai.chat prompt: "When was factory_bot released?",
106
- # examples: [
107
- # {
108
- # role: "user",
109
- # content: "When was Ruby on Rails released?"
110
- # }
111
- # {
112
- # role: "assistant",
113
- # content: "2004"
114
- # },
115
- # ]
116
- #
117
- # @param prompt [String] The prompt to generate a chat completion for
118
- # @param messages [Array<Hash>] The messages that have been sent in the conversation
119
- # @param context [String] An initial context to provide as a system message, ie "You are RubyGPT, a helpful chat bot for helping people learn Ruby"
120
- # @param examples [Array<Hash>] Examples of messages to provide to the model. Useful for Few-Shot Prompting
121
- # @param options [Hash] extra parameters passed to OpenAI::Client#chat
122
- # @yield [Hash] Stream responses back one token at a time
123
- # @return [Langchain::LLM::OpenAIResponse] Response object
124
- #
125
- def chat(prompt: "", messages: [], context: "", examples: [], **options, &block)
126
- raise ArgumentError.new(":prompt or :messages argument is expected") if prompt.empty? && messages.empty?
127
-
128
- parameters = compose_parameters @defaults[:chat_completion_model_name], options, &block
129
- parameters[:messages] = compose_chat_messages(prompt: prompt, messages: messages, context: context, examples: examples)
91
+ # @param messages [Array<Hash>] List of messages comprising the conversation so far
92
+ # @param model [String] ID of the model to use
93
+ def chat(
94
+ messages: [],
95
+ model: defaults[:chat_completion_model_name],
96
+ frequency_penalty: nil,
97
+ logit_bias: nil,
98
+ logprobs: nil,
99
+ top_logprobs: nil,
100
+ max_tokens: nil,
101
+ n: defaults[:n],
102
+ presence_penalty: nil,
103
+ response_format: nil,
104
+ seed: nil,
105
+ stop: nil,
106
+ stream: nil,
107
+ temperature: defaults[:temperature],
108
+ top_p: nil,
109
+ tools: [],
110
+ tool_choice: nil,
111
+ user: nil,
112
+ &block
113
+ )
114
+ raise ArgumentError.new("messages argument is required") if messages.empty?
115
+ raise ArgumentError.new("model argument is required") if model.empty?
116
+ raise ArgumentError.new("'tool_choice' is only allowed when 'tools' are specified.") if tool_choice && tools.empty?
117
+
118
+ parameters = {
119
+ messages: messages,
120
+ model: model
121
+ }
122
+ parameters[:frequency_penalty] = frequency_penalty if frequency_penalty
123
+ parameters[:logit_bias] = logit_bias if logit_bias
124
+ parameters[:logprobs] = logprobs if logprobs
125
+ parameters[:top_logprobs] = top_logprobs if top_logprobs
126
+ # TODO: Fix max_tokens validation to account for tools/functions
127
+ parameters[:max_tokens] = max_tokens if max_tokens # || validate_max_tokens(parameters[:messages], parameters[:model])
128
+ parameters[:n] = n if n
129
+ parameters[:presence_penalty] = presence_penalty if presence_penalty
130
+ parameters[:response_format] = response_format if response_format
131
+ parameters[:seed] = seed if seed
132
+ parameters[:stop] = stop if stop
133
+ parameters[:stream] = stream if stream
134
+ parameters[:temperature] = temperature if temperature
135
+ parameters[:top_p] = top_p if top_p
136
+ parameters[:tools] = tools if tools.any?
137
+ parameters[:tool_choice] = tool_choice if tool_choice
138
+ parameters[:user] = user if user
139
+
140
+ # TODO: Clean this part up
141
+ if block
142
+ @response_chunks = []
143
+ parameters[:stream] = proc do |chunk, _bytesize|
144
+ chunk_content = chunk.dig("choices", 0)
145
+ @response_chunks << chunk
146
+ yield chunk_content
147
+ end
148
+ end
130
149
 
131
- if functions
132
- parameters[:functions] = functions
133
- else
134
- parameters[:max_tokens] = validate_max_tokens(parameters[:messages], parameters[:model], parameters[:max_tokens])
150
+ response = with_api_error_handling do
151
+ client.chat(parameters: parameters)
135
152
  end
136
153
 
137
- response = with_api_error_handling { client.chat(parameters: parameters) }
138
154
  response = response_from_chunks if block
139
155
  reset_response_chunks
156
+
140
157
  Langchain::LLM::OpenAIResponse.new(response)
141
158
  end
142
159
 
143
- #
144
160
  # Generate a summary for a given text
145
161
  #
146
162
  # @param text [String] The text to generate a summary for
147
163
  # @return [String] The summary
148
- #
149
164
  def summarize(text:)
150
165
  prompt_template = Langchain::Prompt.load_from_path(
151
166
  file_path: Langchain.root.join("langchain/llm/prompts/summarize_template.yaml")
152
167
  )
153
168
  prompt = prompt_template.format(text: text)
154
169
 
155
- complete(prompt: prompt, temperature: @defaults[:temperature])
156
- # Should this return a Langchain::LLM::OpenAIResponse as well?
170
+ complete(prompt: prompt)
157
171
  end
158
172
 
159
173
  private
@@ -164,71 +178,6 @@ module Langchain::LLM
164
178
  @response_chunks = []
165
179
  end
166
180
 
167
- def is_legacy_model?(model)
168
- LEGACY_COMPLETION_MODELS.any? { |legacy_model| model.include?(legacy_model) }
169
- end
170
-
171
- def legacy_complete(prompt, parameters)
172
- Langchain.logger.warn "DEPRECATION WARNING: The model #{parameters[:model]} is deprecated. Please use gpt-3.5-turbo instead. Details: https://platform.openai.com/docs/deprecations/2023-07-06-gpt-and-embeddings"
173
-
174
- parameters[:prompt] = prompt
175
- parameters[:max_tokens] = validate_max_tokens(prompt, parameters[:model])
176
-
177
- response = with_api_error_handling do
178
- client.completions(parameters: parameters)
179
- end
180
- response.dig("choices", 0, "text")
181
- end
182
-
183
- def compose_parameters(model, params, &block)
184
- default_params = {model: model, temperature: @defaults[:temperature], n: @defaults[:n]}
185
- default_params[:stop] = params.delete(:stop_sequences) if params[:stop_sequences]
186
- parameters = default_params.merge(params)
187
-
188
- if block
189
- @response_chunks = []
190
- parameters[:stream] = proc do |chunk, _bytesize|
191
- chunk_content = chunk.dig("choices", 0)
192
- @response_chunks << chunk
193
- yield chunk_content
194
- end
195
- end
196
-
197
- parameters
198
- end
199
-
200
- def compose_chat_messages(prompt:, messages: [], context: "", examples: [])
201
- history = []
202
-
203
- history.concat transform_messages(examples) unless examples.empty?
204
-
205
- history.concat transform_messages(messages) unless messages.empty?
206
-
207
- unless context.nil? || context.empty?
208
- history.reject! { |message| message[:role] == "system" }
209
- history.prepend({role: "system", content: context})
210
- end
211
-
212
- unless prompt.empty?
213
- if history.last && history.last[:role] == "user"
214
- history.last[:content] += "\n#{prompt}"
215
- else
216
- history.append({role: "user", content: prompt})
217
- end
218
- end
219
-
220
- history
221
- end
222
-
223
- def transform_messages(messages)
224
- messages.map do |message|
225
- {
226
- role: message[:role],
227
- content: message[:content]
228
- }
229
- end
230
- end
231
-
232
181
  def with_api_error_handling
233
182
  response = yield
234
183
  return if response.empty?
@@ -239,12 +188,7 @@ module Langchain::LLM
239
188
  end
240
189
 
241
190
  def validate_max_tokens(messages, model, max_tokens = nil)
242
- LENGTH_VALIDATOR.validate_max_tokens!(messages, model, max_tokens: max_tokens)
243
- end
244
-
245
- def extract_response(response)
246
- results = response.dig("choices").map { |choice| choice.dig("message", "content") }
247
- (results.size == 1) ? results.first : results
191
+ LENGTH_VALIDATOR.validate_max_tokens!(messages, model, max_tokens: max_tokens, llm: self)
248
192
  end
249
193
 
250
194
  def response_from_chunks
@@ -77,12 +77,6 @@ module Langchain::LLM
77
77
  Langchain::LLM::ReplicateResponse.new(response, model: @defaults[:completion_model_name])
78
78
  end
79
79
 
80
- # Cohere does not have a dedicated chat endpoint, so instead we call `complete()`
81
- def chat(...)
82
- response_text = complete(...)
83
- ::Langchain::Conversation::Response.new(response_text)
84
- end
85
-
86
80
  #
87
81
  # Generate a summary for a given text
88
82
  #
@@ -25,5 +25,9 @@ module Langchain::LLM
25
25
  def log_id
26
26
  raw_response.dig("log_id")
27
27
  end
28
+
29
+ def role
30
+ "assistant"
31
+ end
28
32
  end
29
33
  end
@@ -32,5 +32,9 @@ module Langchain::LLM
32
32
  def embeddings
33
33
  [raw_response.dig("embedding", "value")]
34
34
  end
35
+
36
+ def role
37
+ "assistant"
38
+ end
35
39
  end
36
40
  end
@@ -8,7 +8,7 @@ module Langchain::LLM
8
8
  end
9
9
 
10
10
  def completion
11
- raw_response.first
11
+ completions.first
12
12
  end
13
13
 
14
14
  def completions
@@ -22,5 +22,9 @@ module Langchain::LLM
22
22
  def embeddings
23
23
  [raw_response&.dig("embedding")]
24
24
  end
25
+
26
+ def role
27
+ "assistant"
28
+ end
25
29
  end
26
30
  end
@@ -16,10 +16,18 @@ module Langchain::LLM
16
16
  completions&.dig(0, "message", "content")
17
17
  end
18
18
 
19
+ def role
20
+ completions&.dig(0, "message", "role")
21
+ end
22
+
19
23
  def chat_completion
20
24
  completion
21
25
  end
22
26
 
27
+ def tool_calls
28
+ chat_completions&.dig(0, "message", "tool_calls")
29
+ end
30
+
23
31
  def embedding
24
32
  embeddings&.first
25
33
  end
@@ -37,9 +37,10 @@ module Langchain
37
37
  # @param path [String | Pathname] path to file or URL
38
38
  # @param options [Hash] options passed to the processor class used to process the data
39
39
  # @return [Langchain::Loader] loader instance
40
- def initialize(path, options = {})
40
+ def initialize(path, options = {}, chunker: Langchain::Chunker::Text)
41
41
  @options = options
42
42
  @path = path
43
+ @chunker = chunker
43
44
  end
44
45
 
45
46
  # Is the path a URL?
@@ -112,7 +113,7 @@ module Langchain
112
113
  processor_klass.new(@options).parse(@raw_data)
113
114
  end
114
115
 
115
- Langchain::Data.new(result)
116
+ Langchain::Data.new(result, source: @options[:source], chunker: @chunker)
116
117
  end
117
118
 
118
119
  def processor_klass
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Langchain
4
+ module Processors
5
+ class Markdown < Base
6
+ EXTENSIONS = [".markdown", ".md"]
7
+ CONTENT_TYPES = ["text/markdown"]
8
+
9
+ # Parse the document and return the text
10
+ # @param [File] data
11
+ # @return [String]
12
+ def parse(data)
13
+ data.read
14
+ end
15
+ end
16
+ end
17
+ end