langchainrb 0.8.2 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +57 -27
  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/conversation/memory.rb +1 -6
  8. data/lib/langchain/conversation.rb +7 -18
  9. data/lib/langchain/llm/ai21.rb +1 -1
  10. data/lib/langchain/llm/azure.rb +10 -97
  11. data/lib/langchain/llm/base.rb +1 -0
  12. data/lib/langchain/llm/cohere.rb +4 -6
  13. data/lib/langchain/llm/google_palm.rb +2 -0
  14. data/lib/langchain/llm/google_vertex_ai.rb +12 -10
  15. data/lib/langchain/llm/ollama.rb +167 -27
  16. data/lib/langchain/llm/openai.rb +104 -160
  17. data/lib/langchain/llm/replicate.rb +0 -6
  18. data/lib/langchain/llm/response/anthropic_response.rb +4 -0
  19. data/lib/langchain/llm/response/base_response.rb +7 -0
  20. data/lib/langchain/llm/response/google_palm_response.rb +4 -0
  21. data/lib/langchain/llm/response/ollama_response.rb +22 -0
  22. data/lib/langchain/llm/response/openai_response.rb +8 -0
  23. data/lib/langchain/tool/base.rb +24 -0
  24. data/lib/langchain/tool/google_search.rb +1 -4
  25. data/lib/langchain/utils/token_length/ai21_validator.rb +2 -2
  26. data/lib/langchain/utils/token_length/cohere_validator.rb +2 -2
  27. data/lib/langchain/utils/token_length/google_palm_validator.rb +2 -2
  28. data/lib/langchain/utils/token_length/openai_validator.rb +13 -2
  29. data/lib/langchain/utils/token_length/token_limit_exceeded.rb +1 -1
  30. data/lib/langchain/vectorsearch/pinecone.rb +2 -1
  31. data/lib/langchain/version.rb +1 -1
  32. data/lib/langchain.rb +2 -1
  33. metadata +24 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 13eec34cc529732ddfb8994956659bd4307a79ebfd76ff883fe3b6644d647c24
4
- data.tar.gz: ce04acfe42a6a8da5a5951734651dd0083f7d2efc43cf4b3367710c8221ee96a
3
+ metadata.gz: 4c9d0655d58ddff57b9c9163065908dd17d91c6bffc5b146bf7fc01b4c9fb96d
4
+ data.tar.gz: ee82c644b7e38503fa0587ade2af0447819863303e9fa3755dce1676d68ad5f7
5
5
  SHA512:
6
- metadata.gz: 2094d99610311a1583d890f8c6898605bcd3e76d2fb72deb1ccd4b250f2b98f7a883401faf2e161b97b82fb29f6e64ead8843d8af22f0bd3e8a4c872c150c134
7
- data.tar.gz: d7ce155cbb992e651aa8dc468ed1ee39bd96d1457f50faa11a32d7caac87086f5d8a381fc2b50aaba10ac934486ed415d5e609f47ee0426b4187540e2436b2e9
6
+ metadata.gz: 05faddd31c819e6d351ed99e05353e462341ab1744769a1b3a9932c37de4c68907b54f79bcd65f6b652d954d5301e80055c5ee8c57b66a3917256918c51cc61f
7
+ data.tar.gz: c2fed05da349fdc9ebd9990ea5c2d5c70a68241c491c903c631eb0584bce01da17bab7b04c59fa9fded8282547798219b2a0b951c1c5a8d1062d08f2a930062c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.9.1]
4
+ - Add support for new OpenAI models
5
+ - Add Ollama#chat method
6
+ - Fix and refactor of `Langchain::LLM::Ollama`, responses can now be streamed.
7
+
8
+ ## [0.9.0]
9
+ - Introducing new `Langchain::Assistant` that will be replacing `Langchain::Conversation` and `Langchain::Agent`s.
10
+ - `Langchain::Conversation` is deprecated.
11
+
3
12
  ## [0.8.2]
4
13
  - Introducing new `Langchain::Chunker::Markdown` chunker (thanks @spikex)
5
14
  - Fixes
data/README.md CHANGED
@@ -1,6 +1,3 @@
1
- # Please fill out the [Ruby AI Survey 2023](https://docs.google.com/forms/d/1dH_0js1wpEyh1YqPTOxU3b5fXj76sb5lYp12lVoNNZE/edit).
2
- Results will be anonymized and shared!
3
-
4
1
  💎🔗 Langchain.rb
5
2
  ---
6
3
  ⚡ Building LLM-powered applications in Ruby ⚡
@@ -18,8 +15,7 @@ Available for paid consulting engagements! [Email me](mailto:andrei@sourcelabs.i
18
15
 
19
16
  ## Use Cases
20
17
  * Retrieval Augmented Generation (RAG) and vector search
21
- * Chat bots
22
- * [AI agents](https://github.com/andreibondarev/langchainrb/tree/main/lib/langchain/agent/agents.md)
18
+ * [Assistants](#assistants) (chat bots) & [AI Agents](https://github.com/andreibondarev/langchainrb/tree/main/lib/langchain/agent/agents.md)
23
19
 
24
20
  ## Table of Contents
25
21
 
@@ -29,7 +25,7 @@ Available for paid consulting engagements! [Email me](mailto:andrei@sourcelabs.i
29
25
  - [Prompt Management](#prompt-management)
30
26
  - [Output Parsers](#output-parsers)
31
27
  - [Building RAG](#building-retrieval-augment-generation-rag-system)
32
- - [Building chat bots](#building-chat-bots)
28
+ - [Assistants](#assistants)
33
29
  - [Evaluations](#evaluations-evals)
34
30
  - [Examples](#examples)
35
31
  - [Logging](#logging)
@@ -46,6 +42,8 @@ If bundler is not being used to manage dependencies, install the gem by executin
46
42
 
47
43
  gem install langchainrb
48
44
 
45
+ Additional gems may be required when loading LLM Providers. These are not included by default so you can include only what you need.
46
+
49
47
  ## Usage
50
48
 
51
49
  ```ruby
@@ -64,16 +62,16 @@ Langchain.rb wraps all supported LLMs in a unified interface allowing you to eas
64
62
  | [AWS Bedrock](https://aws.amazon.com/bedrock?utm_source=langchainrb&utm_medium=github) | ✅ | ✅ | ❌ | ❌ | Provides AWS, Cohere, AI21, Antropic and Stability AI models |
65
63
  | [Cohere](https://cohere.com/?utm_source=langchainrb&utm_medium=github) | ✅ | ✅ | ✅ | ✅ | |
66
64
  | [GooglePalm](https://ai.google/discover/palm2?utm_source=langchainrb&utm_medium=github) | ✅ | ✅ | ✅ | ✅ | |
67
- | [Google Vertex AI](https://cloud.google.com/vertex-ai?utm_source=langchainrb&utm_medium=github) | ✅ | | ❌ | | |
65
+ | [Google Vertex AI](https://cloud.google.com/vertex-ai?utm_source=langchainrb&utm_medium=github) | ✅ | | ❌ | | |
68
66
  | [HuggingFace](https://huggingface.co/?utm_source=langchainrb&utm_medium=github) | ✅ | ❌ | ❌ | ❌ | |
69
- | [Ollama](https://ollama.ai/?utm_source=langchainrb&utm_medium=github) | ✅ | ✅ | | ❌ | |
67
+ | [Ollama](https://ollama.ai/?utm_source=langchainrb&utm_medium=github) | ✅ | ✅ | | ❌ | |
70
68
  | [Replicate](https://replicate.com/?utm_source=langchainrb&utm_medium=github) | ✅ | ✅ | ✅ | ✅ | |
71
69
 
72
70
  #### Using standalone LLMs:
73
71
 
74
72
  #### OpenAI
75
73
 
76
- Add `gem "ruby-openai", "~> 6.1.0"` to your Gemfile.
74
+ Add `gem "ruby-openai", "~> 6.3.0"` to your Gemfile.
77
75
 
78
76
  ```ruby
79
77
  llm = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
@@ -95,7 +93,7 @@ llm.complete(prompt: "What is the meaning of life?").completion
95
93
 
96
94
  Generate a chat completion:
97
95
  ```ruby
98
- llm.chat(prompt: "Hey! How are you?").completion
96
+ llm.chat(messages: [{role: "user", content: "What is the meaning of life?"}]).completion
99
97
  ```
100
98
 
101
99
  Summarize the text:
@@ -405,43 +403,75 @@ client.ask(
405
403
  )
406
404
  ```
407
405
 
408
- ## Building chat bots
406
+ ## Evaluations (Evals)
407
+ The Evaluations module is a collection of tools that can be used to evaluate and track the performance of the output products by LLM and your RAG (Retrieval Augmented Generation) pipelines.
409
408
 
410
- ### Conversation class
409
+ ## Assistants
410
+ Assistants are Agent-like objects that leverage helpful instructions, LLMs, tools and knowledge to respond to user queries. Assistants can be configured with an LLM of your choice (currently only OpenAI), any vector search database and easily extended with additional tools.
411
411
 
412
- Choose and instantiate the LLM provider you'll be using:
412
+ ### Creating an Assistant
413
+ 1. Instantiate an LLM of your choice
413
414
  ```ruby
414
415
  llm = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
415
416
  ```
416
- Instantiate the Conversation class:
417
+ 2. Instantiate a Thread. Threads keep track of the messages in the Assistant conversation.
418
+ ```ruby
419
+ thread = Langchain::Thread.new
420
+ ```
421
+ You can pass old message from previously using the Assistant:
417
422
  ```ruby
418
- chat = Langchain::Conversation.new(llm: llm)
423
+ thread.messages = messages
419
424
  ```
425
+ Messages contain the conversation history and the whole message history is sent to the LLM every time. A Message belongs to 1 of the 4 roles:
426
+ * `Message(role: "system")` message usually contains the instructions.
427
+ * `Message(role: "user")` messages come from the user.
428
+ * `Message(role: "assistant")` messages are produced by the LLM.
429
+ * `Message(role: "tool")` messages are sent in response to tool calls with tool outputs.
420
430
 
421
- (Optional) Set the conversation context:
431
+ 3. Instantiate an Assistant
422
432
  ```ruby
423
- chat.set_context("You are a chatbot from the future")
433
+ assistant = Langchain::Assistant.new(
434
+ llm: llm,
435
+ thread: thread,
436
+ instructions: "You are a Meteorologist Assistant that is able to pull the weather for any location",
437
+ tools: [
438
+ Langchain::Tool::GoogleSearch.new(api_key: ENV["SERPAPI_API_KEY"])
439
+ ]
440
+ )
441
+ ```
442
+ ### Using an Assistant
443
+ You can now add your message to an Assistant.
444
+ ```ruby
445
+ assistant.add_message content: "What's the weather in New York City?"
424
446
  ```
425
447
 
426
- Exchange messages with the LLM
448
+ Run the Assistant to generate a response.
427
449
  ```ruby
428
- chat.message("Tell me about future technologies")
450
+ assistant.run
429
451
  ```
430
452
 
431
- To stream the chat response:
453
+ If a Tool is invoked you can manually submit an output.
432
454
  ```ruby
433
- chat = Langchain::Conversation.new(llm: llm) do |chunk|
434
- print(chunk)
435
- end
455
+ assistant.submit_tool_output tool_call_id: "...", output: "It's 70 degrees and sunny in New York City"
436
456
  ```
437
457
 
438
- Open AI Functions support
458
+ Or run the assistant with `auto_tool_execution: tool` to call Tools automatically.
439
459
  ```ruby
440
- chat.set_functions(functions)
460
+ assistant.add_message content: "How about San Diego, CA?"
461
+ assistant.run(auto_tool_execution: true)
462
+ ```
463
+ You can also combine the two by calling:
464
+ ```ruby
465
+ assistant.add_message_and_run content: "What about Sacramento, CA?", auto_tool_execution: true
441
466
  ```
442
467
 
443
- ## Evaluations (Evals)
444
- The Evaluations module is a collection of tools that can be used to evaluate and track the performance of the output products by LLM and your RAG (Retrieval Augmented Generation) pipelines.
468
+ ### Accessing Thread messages
469
+ You can access the messages in a Thread by calling `assistant.thread.messages`.
470
+ ```ruby
471
+ assistant.thread.messages
472
+ ```
473
+
474
+ The Assistant checks the context window limits before every request to the LLM and remove oldest thread messages one by one if the context window is exceeded.
445
475
 
446
476
  ### RAGAS
447
477
  Ragas helps you evaluate your Retrieval Augmented Generation (RAG) pipelines. The implementation is based on this [paper](https://arxiv.org/abs/2309.15217) and the original Python [repo](https://github.com/explodinggradients/ragas). Ragas tracks the following 3 metrics and assigns the 0.0 - 1.0 scores:
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Langchain
4
+ class Assistant
5
+ attr_reader :llm, :thread, :instructions
6
+ attr_accessor :tools
7
+
8
+ # Create a new assistant
9
+ #
10
+ # @param llm [Langchain::LLM::Base] LLM instance that the assistant will use
11
+ # @param thread [Langchain::Thread] The thread that'll keep track of the conversation
12
+ # @param tools [Array<Langchain::Tool::Base>] Tools that the assistant has access to
13
+ # @param instructions [String] The system instructions to include in the thread
14
+ def initialize(
15
+ llm:,
16
+ thread:,
17
+ tools: [],
18
+ instructions: nil
19
+ )
20
+ raise ArgumentError, "Invalid LLM; currently only Langchain::LLM::OpenAI is supported" unless llm.instance_of?(Langchain::LLM::OpenAI)
21
+ raise ArgumentError, "Thread must be an instance of Langchain::Thread" unless thread.is_a?(Langchain::Thread)
22
+ raise ArgumentError, "Tools must be an array of Langchain::Tool::Base instance(s)" unless tools.is_a?(Array) && tools.all? { |tool| tool.is_a?(Langchain::Tool::Base) }
23
+
24
+ @llm = llm
25
+ @thread = thread
26
+ @tools = tools
27
+ @instructions = instructions
28
+
29
+ # The first message in the thread should be the system instructions
30
+ # TODO: What if the user added old messages and the system instructions are already in there? Should this overwrite the existing instructions?
31
+ add_message(role: "system", content: instructions) if instructions
32
+ end
33
+
34
+ # Add a user message to the thread
35
+ #
36
+ # @param content [String] The content of the message
37
+ # @param role [String] The role attribute of the message. Default: "user"
38
+ # @param tool_calls [Array<Hash>] The tool calls to include in the message
39
+ # @param tool_call_id [String] The ID of the tool call to include in the message
40
+ # @return [Array<Langchain::Message>] The messages in the thread
41
+ def add_message(content: nil, role: "user", tool_calls: [], tool_call_id: nil)
42
+ message = build_message(role: role, content: content, tool_calls: tool_calls, tool_call_id: tool_call_id)
43
+ thread.add_message(message)
44
+ end
45
+
46
+ # Run the assistant
47
+ #
48
+ # @param auto_tool_execution [Boolean] Whether or not to automatically run tools
49
+ # @return [Array<Langchain::Message>] The messages in the thread
50
+ def run(auto_tool_execution: false)
51
+ running = true
52
+
53
+ while running
54
+ # TODO: I think we need to look at all messages and not just the last one.
55
+ case (last_message = thread.messages.last).role
56
+ when "system"
57
+ # Do nothing
58
+ running = false
59
+ when "assistant"
60
+ if last_message.tool_calls.any?
61
+ if auto_tool_execution
62
+ run_tools(last_message.tool_calls)
63
+ else
64
+ # Maybe log and tell the user that there's outstanding tool calls?
65
+ running = false
66
+ end
67
+ else
68
+ # Last message was from the assistant without any tools calls.
69
+ # Do nothing
70
+ running = false
71
+ end
72
+ when "user"
73
+ # Run it!
74
+ response = chat_with_llm
75
+
76
+ if response.tool_calls
77
+ # Re-run the while(running) loop to process the tool calls
78
+ running = true
79
+ add_message(role: response.role, tool_calls: response.tool_calls)
80
+ elsif response.chat_completion
81
+ # Stop the while(running) loop and add the assistant's response to the thread
82
+ running = false
83
+ add_message(role: response.role, content: response.chat_completion)
84
+ end
85
+ when "tool"
86
+ # Run it!
87
+ response = chat_with_llm
88
+ running = true
89
+
90
+ if response.tool_calls
91
+ add_message(role: response.role, tool_calls: response.tool_calls)
92
+ elsif response.chat_completion
93
+ add_message(role: response.role, content: response.chat_completion)
94
+ end
95
+ end
96
+ end
97
+
98
+ thread.messages
99
+ end
100
+
101
+ # Add a user message to the thread and run the assistant
102
+ #
103
+ # @param content [String] The content of the message
104
+ # @param auto_tool_execution [Boolean] Whether or not to automatically run tools
105
+ # @return [Array<Langchain::Message>] The messages in the thread
106
+ def add_message_and_run(content:, auto_tool_execution: false)
107
+ add_message(content: content, role: "user")
108
+ run(auto_tool_execution: auto_tool_execution)
109
+ end
110
+
111
+ # Submit tool output to the thread
112
+ #
113
+ # @param tool_call_id [String] The ID of the tool call to submit output for
114
+ # @param output [String] The output of the tool
115
+ # @return [Array<Langchain::Message>] The messages in the thread
116
+ def submit_tool_output(tool_call_id:, output:)
117
+ # TODO: Validate that `tool_call_id` is valid
118
+ add_message(role: "tool", content: output, tool_call_id: tool_call_id)
119
+ end
120
+
121
+ private
122
+
123
+ # Call to the LLM#chat() method
124
+ #
125
+ # @return [Langchain::LLM::BaseResponse] The LLM response object
126
+ def chat_with_llm
127
+ params = {messages: thread.openai_messages}
128
+
129
+ if tools.any?
130
+ params[:tools] = tools.map(&:to_openai_tool)
131
+ # TODO: Not sure that tool_choice should always be "auto"; Maybe we can let the user toggle it.
132
+ params[:tool_choice] = "auto"
133
+ end
134
+
135
+ llm.chat(**params)
136
+ end
137
+
138
+ # Run the tools automatically
139
+ #
140
+ # @param tool_calls [Array<Hash>] The tool calls to run
141
+ def run_tools(tool_calls)
142
+ # Iterate over each function invocation and submit tool output
143
+ tool_calls.each do |tool_call|
144
+ tool_call_id = tool_call.dig("id")
145
+ tool_name = tool_call.dig("function", "name")
146
+ tool_arguments = JSON.parse(tool_call.dig("function", "arguments"), symbolize_names: true)
147
+
148
+ tool_instance = tools.find do |t|
149
+ t.name == tool_name
150
+ end or raise ArgumentError, "Tool not found in assistant.tools"
151
+
152
+ output = tool_instance.execute(**tool_arguments)
153
+
154
+ submit_tool_output(tool_call_id: tool_call_id, output: output)
155
+ end
156
+
157
+ response = chat_with_llm
158
+
159
+ if response.tool_calls
160
+ add_message(role: response.role, tool_calls: response.tool_calls)
161
+ elsif response.chat_completion
162
+ add_message(role: response.role, content: response.chat_completion)
163
+ end
164
+ end
165
+
166
+ # Build a message
167
+ #
168
+ # @param role [String] The role of the message
169
+ # @param content [String] The content of the message
170
+ # @param tool_calls [Array<Hash>] The tool calls to include in the message
171
+ # @param tool_call_id [String] The ID of the tool call to include in the message
172
+ # @return [Langchain::Message] The Message object
173
+ def build_message(role:, content: nil, tool_calls: [], tool_call_id: nil)
174
+ Message.new(role: role, content: content, tool_calls: tool_calls, tool_call_id: tool_call_id)
175
+ end
176
+
177
+ # # TODO: Fix the message truncation when context window is exceeded
178
+ # def build_assistant_prompt(instructions:, tools:)
179
+ # while begin
180
+ # # Check if the prompt exceeds the context window
181
+ # # Return false to exit the while loop
182
+ # !llm.class.const_get(:LENGTH_VALIDATOR).validate_max_tokens!(
183
+ # thread.messages,
184
+ # llm.defaults[:chat_completion_model_name],
185
+ # {llm: llm}
186
+ # )
187
+ # # Rescue error if context window is exceeded and return true to continue the while loop
188
+ # rescue Langchain::Utils::TokenLength::TokenLimitExceeded
189
+ # # Should be using `retry` instead of while()
190
+ # true
191
+ # end
192
+ # # Truncate the oldest messages when the context window is exceeded
193
+ # thread.messages.shift
194
+ # end
195
+
196
+ # prompt
197
+ # end
198
+ end
199
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Langchain
4
+ # Langchain::Message are the messages that are sent to LLM chat methods
5
+ class Message
6
+ attr_reader :role, :content, :tool_calls, :tool_call_id
7
+
8
+ ROLES = %w[
9
+ system
10
+ assistant
11
+ user
12
+ tool
13
+ ].freeze
14
+
15
+ # @param role [String] The role of the message
16
+ # @param content [String] The content of the message
17
+ # @param tool_calls [Array<Hash>] Tool calls to be made
18
+ # @param tool_call_id [String] The ID of the tool call to be made
19
+ def initialize(role:, content: nil, tool_calls: [], tool_call_id: nil) # TODO: Implement image_file: reference (https://platform.openai.com/docs/api-reference/messages/object#messages/object-content)
20
+ raise ArgumentError, "Role must be one of #{ROLES.join(", ")}" unless ROLES.include?(role)
21
+ 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) }
22
+
23
+ @role = role
24
+ # Some Tools return content as a JSON hence `.to_s`
25
+ @content = content.to_s
26
+ @tool_calls = tool_calls
27
+ @tool_call_id = tool_call_id
28
+ end
29
+
30
+ # Convert the message to an OpenAI API-compatible hash
31
+ #
32
+ # @return [Hash] The message as an OpenAI API-compatible hash
33
+ def to_openai_format
34
+ {}.tap do |h|
35
+ h[:role] = role
36
+ h[:content] = content if content # Content is nil for tool calls
37
+ h[:tool_calls] = tool_calls if tool_calls.any?
38
+ h[:tool_call_id] = tool_call_id if tool_call_id
39
+ end
40
+ end
41
+
42
+ def assistant?
43
+ role == "assistant"
44
+ end
45
+
46
+ def system?
47
+ role == "system"
48
+ end
49
+
50
+ def user?
51
+ role == "user"
52
+ end
53
+
54
+ def tool?
55
+ role == "tool"
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Langchain
4
+ # Langchain::Thread keeps track of messages in a conversation
5
+ # Eventually we may want to add functionality to persist to the thread to disk, DB, storage, etc.
6
+ class Thread
7
+ attr_accessor :messages
8
+
9
+ # @param messages [Array<Langchain::Message>]
10
+ def initialize(messages: [])
11
+ raise ArgumentError, "messages array must only contain Langchain::Message instance(s)" unless messages.is_a?(Array) && messages.all? { |m| m.is_a?(Langchain::Message) }
12
+
13
+ @messages = messages
14
+ end
15
+
16
+ # Convert the thread to an OpenAI API-compatible array of hashes
17
+ #
18
+ # @return [Array<Hash>] The thread as an OpenAI API-compatible array of hashes
19
+ def openai_messages
20
+ messages.map(&:to_openai_format)
21
+ end
22
+
23
+ # Add a message to the thread
24
+ #
25
+ # @param message [Langchain::Message] The message to add
26
+ # @return [Array<Langchain::Message>] The updated messages array
27
+ def add_message(message)
28
+ raise ArgumentError, "message must be a Langchain::Message instance" unless message.is_a?(Langchain::Message)
29
+
30
+ # Prepend the message to the thread
31
+ messages << message
32
+ end
33
+ end
34
+ end
@@ -3,7 +3,7 @@
3
3
  module Langchain
4
4
  class Conversation
5
5
  class Memory
6
- attr_reader :examples, :messages
6
+ attr_reader :messages
7
7
 
8
8
  # The least number of tokens we want to be under the limit by
9
9
  TOKEN_LEEWAY = 20
@@ -12,7 +12,6 @@ module Langchain
12
12
  @llm = llm
13
13
  @context = nil
14
14
  @summary = nil
15
- @examples = []
16
15
  @messages = messages
17
16
  @strategy = options.delete(:strategy) || :truncate
18
17
  @options = options
@@ -22,10 +21,6 @@ module Langchain
22
21
  @context = message
23
22
  end
24
23
 
25
- def add_examples(examples)
26
- @examples.concat examples
27
- end
28
-
29
24
  def append_message(message)
30
25
  @messages.append(message)
31
26
  end
@@ -25,9 +25,10 @@ module Langchain
25
25
  # @param options [Hash] Options to pass to the LLM, like temperature, top_k, etc.
26
26
  # @return [Langchain::Conversation] The Langchain::Conversation instance
27
27
  def initialize(llm:, **options, &block)
28
+ warn "[DEPRECATION] `Langchain::Conversation` is deprecated. Please use `Langchain::Assistant` instead."
29
+
28
30
  @llm = llm
29
31
  @context = nil
30
- @examples = []
31
32
  @memory = ::Langchain::Conversation::Memory.new(
32
33
  llm: llm,
33
34
  messages: options.delete(:messages) || [],
@@ -37,22 +38,12 @@ module Langchain
37
38
  @block = block
38
39
  end
39
40
 
40
- def set_functions(functions)
41
- @llm.functions = functions
42
- end
43
-
44
41
  # Set the context of the conversation. Usually used to set the model's persona.
45
42
  # @param message [String] The context of the conversation
46
43
  def set_context(message)
47
44
  @memory.set_context ::Langchain::Conversation::Context.new(message)
48
45
  end
49
46
 
50
- # Add examples to the conversation. Used to give the model a sense of the conversation.
51
- # @param examples [Array<Prompt|Response>] The examples to add to the conversation
52
- def add_examples(examples)
53
- @memory.add_examples examples
54
- end
55
-
56
47
  # Message the model with a prompt and return the response.
57
48
  # @param message [String] The prompt to message the model with
58
49
  # @return [Response] The response from the model
@@ -75,16 +66,14 @@ module Langchain
75
66
  @memory.context
76
67
  end
77
68
 
78
- # Examples from conversation memory
79
- # @return [Array<Prompt|Response>] Examples from the conversation memory
80
- def examples
81
- @memory.examples
82
- end
83
-
84
69
  private
85
70
 
86
71
  def llm_response
87
- @llm.chat(messages: @memory.messages.map(&:to_h), context: @memory.context&.to_s, examples: @memory.examples.map(&:to_h), **@options, &@block)
72
+ message_history = messages.map(&:to_h)
73
+ # Prepend the system message as context as the first message
74
+ message_history.prepend({role: "system", content: @memory.context.to_s}) if @memory.context
75
+
76
+ @llm.chat(messages: message_history, **@options, &@block)
88
77
  rescue Langchain::Utils::TokenLength::TokenLimitExceeded => exception
89
78
  @memory.reduce_messages(exception)
90
79
  retry
@@ -35,7 +35,7 @@ module Langchain::LLM
35
35
  def complete(prompt:, **params)
36
36
  parameters = complete_parameters params
37
37
 
38
- parameters[:maxTokens] = LENGTH_VALIDATOR.validate_max_tokens!(prompt, parameters[:model], client)
38
+ parameters[:maxTokens] = LENGTH_VALIDATOR.validate_max_tokens!(prompt, parameters[:model], {llm: client})
39
39
 
40
40
  response = client.complete(prompt, parameters)
41
41
  Langchain::LLM::AI21Response.new response, model: parameters[:model]