langchainrb 0.6.16 → 0.6.17
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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/langchain/chunker/base.rb +3 -0
- data/lib/langchain/chunker/prompts/semantic_prompt_template.yml +8 -0
- data/lib/langchain/chunker/semantic.rb +49 -0
- data/lib/langchain/{ai_message.rb → conversation/context.rb} +2 -3
- data/lib/langchain/conversation/memory.rb +86 -0
- data/lib/langchain/conversation/message.rb +48 -0
- data/lib/langchain/{human_message.rb → conversation/prompt.rb} +2 -3
- data/lib/langchain/{system_message.rb → conversation/response.rb} +2 -3
- data/lib/langchain/conversation.rb +11 -12
- data/lib/langchain/llm/cohere.rb +1 -1
- data/lib/langchain/llm/google_palm.rb +12 -12
- data/lib/langchain/llm/openai.rb +33 -37
- data/lib/langchain/llm/replicate.rb +1 -1
- data/lib/langchain/vectorsearch/chroma.rb +6 -7
- data/lib/langchain/version.rb +1 -1
- data/lib/langchain.rb +0 -1
- metadata +13 -11
- data/lib/langchain/conversation_memory.rb +0 -84
- data/lib/langchain/message.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b9bca59bfb5909f6ac24ebf6dba6074f5faf3d2cdadab1a3b3a8a0f75f98adc
|
4
|
+
data.tar.gz: a202726d383d2dc691cb4146e9b36cb7ea6f8ac35382a3df67f6e11d35b3562e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4eaf631f22236035c9e29b3618a70d14487cc9e39b6885e44497ebad2a98670ce88997fdb25144b6467e0caa69a04ce7e625c9e10bc88322131181c2254a570
|
7
|
+
data.tar.gz: 981199fe2a0123e46ac3af54946c03d5eaa827473eae02f2e60accd0c680a0bbd40741800e05b79e890038523a1b910502a6cf4ed1f4ebf77845f4b2a2dbc5d9
|
data/README.md
CHANGED
@@ -59,7 +59,7 @@ client = Langchain::Vectorsearch::Weaviate.new(
|
|
59
59
|
)
|
60
60
|
|
61
61
|
# You can instantiate any other supported vector search database:
|
62
|
-
client = Langchain::Vectorsearch::Chroma.new(...) # `gem "chroma-db", "~> 0.
|
62
|
+
client = Langchain::Vectorsearch::Chroma.new(...) # `gem "chroma-db", "~> 0.6.0"`
|
63
63
|
client = Langchain::Vectorsearch::Hnswlib.new(...) # `gem "hnswlib", "~> 0.8.1"`
|
64
64
|
client = Langchain::Vectorsearch::Milvus.new(...) # `gem "milvus", "~> 0.9.2"`
|
65
65
|
client = Langchain::Vectorsearch::Pinecone.new(...) # `gem "pinecone", "~> 0.1.6"`
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Langchain
|
4
|
+
module Chunker
|
5
|
+
#
|
6
|
+
# LLM-powered semantic chunker.
|
7
|
+
# Semantic chunking is a technique of splitting texts by their semantic meaning, e.g.: themes, topics, and ideas.
|
8
|
+
# We use an LLM to accomplish this. The Anthropic LLM is highly recommended for this task as it has the longest context window (100k tokens).
|
9
|
+
#
|
10
|
+
# Usage:
|
11
|
+
# Langchain::Chunker::Semantic.new(
|
12
|
+
# text,
|
13
|
+
# llm: Langchain::LLM::Anthropic.new(api_key: ENV["ANTHROPIC_API_KEY"])
|
14
|
+
# ).chunks
|
15
|
+
#
|
16
|
+
class Semantic < Base
|
17
|
+
attr_reader :text, :llm, :prompt_template
|
18
|
+
# @param [Langchain::LLM::Base] Langchain::LLM::* instance
|
19
|
+
# @param [Langchain::Prompt::PromptTemplate] Optional custom prompt template
|
20
|
+
def initialize(text, llm:, prompt_template: nil)
|
21
|
+
@text = text
|
22
|
+
@llm = llm
|
23
|
+
@prompt_template = prompt_template || default_prompt_template
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Array<String>]
|
27
|
+
def chunks
|
28
|
+
prompt = prompt_template.format(text: text)
|
29
|
+
|
30
|
+
# Replace static 50k limit with dynamic limit based on text length (max_tokens_to_sample)
|
31
|
+
completion = llm.complete(prompt: prompt, max_tokens_to_sample: 50000)
|
32
|
+
completion
|
33
|
+
.gsub("Here are the paragraphs split by topic:\n\n", "")
|
34
|
+
.split("---")
|
35
|
+
.map(&:strip)
|
36
|
+
.reject(&:empty?)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# @return [Langchain::Prompt::PromptTemplate] Default prompt template for semantic chunking
|
42
|
+
def default_prompt_template
|
43
|
+
Langchain::Prompt.load_from_path(
|
44
|
+
file_path: Langchain.root.join("langchain/chunker/prompts/semantic_prompt_template.yml")
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Langchain
|
4
|
+
class Conversation
|
5
|
+
class Memory
|
6
|
+
attr_reader :examples, :messages
|
7
|
+
|
8
|
+
# The least number of tokens we want to be under the limit by
|
9
|
+
TOKEN_LEEWAY = 20
|
10
|
+
|
11
|
+
def initialize(llm:, messages: [], **options)
|
12
|
+
@llm = llm
|
13
|
+
@context = nil
|
14
|
+
@summary = nil
|
15
|
+
@examples = []
|
16
|
+
@messages = messages
|
17
|
+
@strategy = options.delete(:strategy) || :truncate
|
18
|
+
@options = options
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_context(message)
|
22
|
+
@context = message
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_examples(examples)
|
26
|
+
@examples.concat examples
|
27
|
+
end
|
28
|
+
|
29
|
+
def append_message(message)
|
30
|
+
@messages.append(message)
|
31
|
+
end
|
32
|
+
|
33
|
+
def reduce_messages(exception)
|
34
|
+
case @strategy
|
35
|
+
when :truncate
|
36
|
+
truncate_messages(exception)
|
37
|
+
when :summarize
|
38
|
+
summarize_messages
|
39
|
+
else
|
40
|
+
raise "Unknown strategy: #{@options[:strategy]}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def context
|
45
|
+
return if @context.nil? && @summary.nil?
|
46
|
+
|
47
|
+
Context.new([@context, @summary].compact.join("\n"))
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def truncate_messages(exception)
|
53
|
+
raise exception if @messages.size == 1
|
54
|
+
|
55
|
+
token_overflow = exception.token_overflow
|
56
|
+
|
57
|
+
@messages = @messages.drop_while do |message|
|
58
|
+
proceed = token_overflow > -TOKEN_LEEWAY
|
59
|
+
token_overflow -= token_length(message.to_json, model_name, llm: @llm)
|
60
|
+
|
61
|
+
proceed
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def summarize_messages
|
66
|
+
history = [@summary, @messages.to_json].compact.join("\n")
|
67
|
+
partitions = [history[0, history.size / 2], history[history.size / 2, history.size]]
|
68
|
+
|
69
|
+
@summary = partitions.map { |messages| @llm.summarize(text: messages.to_json) }.join("\n")
|
70
|
+
|
71
|
+
@messages = [@messages.last]
|
72
|
+
end
|
73
|
+
|
74
|
+
def partition_messages
|
75
|
+
end
|
76
|
+
|
77
|
+
def model_name
|
78
|
+
@llm.class::DEFAULTS[:chat_completion_model_name]
|
79
|
+
end
|
80
|
+
|
81
|
+
def token_length(content, model_name, options)
|
82
|
+
@llm.class::LENGTH_VALIDATOR.token_length(content, model_name, options)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Langchain
|
4
|
+
class Conversation
|
5
|
+
class Message
|
6
|
+
attr_reader :content
|
7
|
+
|
8
|
+
ROLE_MAPPING = {
|
9
|
+
context: "system",
|
10
|
+
prompt: "user",
|
11
|
+
response: "assistant"
|
12
|
+
}
|
13
|
+
|
14
|
+
def initialize(content)
|
15
|
+
@content = content
|
16
|
+
end
|
17
|
+
|
18
|
+
def role
|
19
|
+
ROLE_MAPPING[type]
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
content
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_h
|
27
|
+
{
|
28
|
+
role: role,
|
29
|
+
content: content
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def ==(other)
|
34
|
+
to_json == other.to_json
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_json(options = {})
|
38
|
+
to_h.to_json
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def type
|
44
|
+
self.class.to_s.split("::").last.downcase.to_sym
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -28,7 +28,7 @@ module Langchain
|
|
28
28
|
@llm = llm
|
29
29
|
@context = nil
|
30
30
|
@examples = []
|
31
|
-
@memory =
|
31
|
+
@memory = ::Langchain::Conversation::Memory.new(
|
32
32
|
llm: llm,
|
33
33
|
messages: options.delete(:messages) || [],
|
34
34
|
strategy: options.delete(:memory_strategy)
|
@@ -44,48 +44,47 @@ module Langchain
|
|
44
44
|
# Set the context of the conversation. Usually used to set the model's persona.
|
45
45
|
# @param message [String] The context of the conversation
|
46
46
|
def set_context(message)
|
47
|
-
@memory.set_context
|
47
|
+
@memory.set_context ::Langchain::Conversation::Context.new(message)
|
48
48
|
end
|
49
49
|
|
50
50
|
# Add examples to the conversation. Used to give the model a sense of the conversation.
|
51
|
-
# @param examples [Array<
|
51
|
+
# @param examples [Array<Prompt|Response>] The examples to add to the conversation
|
52
52
|
def add_examples(examples)
|
53
53
|
@memory.add_examples examples
|
54
54
|
end
|
55
55
|
|
56
56
|
# Message the model with a prompt and return the response.
|
57
57
|
# @param message [String] The prompt to message the model with
|
58
|
-
# @return [
|
58
|
+
# @return [Response] The response from the model
|
59
59
|
def message(message)
|
60
|
-
|
61
|
-
|
62
|
-
ai_message = llm_response(human_message)
|
60
|
+
@memory.append_message ::Langchain::Conversation::Prompt.new(message)
|
61
|
+
ai_message = ::Langchain::Conversation::Response.new(llm_response)
|
63
62
|
@memory.append_message(ai_message)
|
64
63
|
ai_message
|
65
64
|
end
|
66
65
|
|
67
66
|
# Messages from conversation memory
|
68
|
-
# @return [Array<
|
67
|
+
# @return [Array<Prompt|Response>] The messages from the conversation memory
|
69
68
|
def messages
|
70
69
|
@memory.messages
|
71
70
|
end
|
72
71
|
|
73
72
|
# Context from conversation memory
|
74
|
-
# @return [
|
73
|
+
# @return [Context] Context from conversation memory
|
75
74
|
def context
|
76
75
|
@memory.context
|
77
76
|
end
|
78
77
|
|
79
78
|
# Examples from conversation memory
|
80
|
-
# @return [Array<
|
79
|
+
# @return [Array<Prompt|Response>] Examples from the conversation memory
|
81
80
|
def examples
|
82
81
|
@memory.examples
|
83
82
|
end
|
84
83
|
|
85
84
|
private
|
86
85
|
|
87
|
-
def llm_response
|
88
|
-
@llm.chat(messages: @memory.messages, context: @memory.context, examples: @memory.examples, **@options, &@block)
|
86
|
+
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)
|
89
88
|
rescue Langchain::Utils::TokenLength::TokenLimitExceeded => exception
|
90
89
|
@memory.reduce_messages(exception)
|
91
90
|
retry
|
data/lib/langchain/llm/cohere.rb
CHANGED
@@ -70,7 +70,7 @@ module Langchain::LLM
|
|
70
70
|
# Cohere does not have a dedicated chat endpoint, so instead we call `complete()`
|
71
71
|
def chat(...)
|
72
72
|
response_text = complete(...)
|
73
|
-
Langchain::
|
73
|
+
::Langchain::Conversation::Response.new(response_text)
|
74
74
|
end
|
75
75
|
|
76
76
|
# Generate a summary in English for a given text
|
@@ -20,7 +20,7 @@ module Langchain::LLM
|
|
20
20
|
}.freeze
|
21
21
|
LENGTH_VALIDATOR = Langchain::Utils::TokenLength::GooglePalmValidator
|
22
22
|
ROLE_MAPPING = {
|
23
|
-
"
|
23
|
+
"assistant" => "ai"
|
24
24
|
}
|
25
25
|
|
26
26
|
def initialize(api_key:, default_options: {})
|
@@ -74,12 +74,12 @@ module Langchain::LLM
|
|
74
74
|
#
|
75
75
|
# Generate a chat completion for a given prompt
|
76
76
|
#
|
77
|
-
# @param prompt [
|
78
|
-
# @param messages [Array<
|
79
|
-
# @param context [
|
80
|
-
# @param examples [Array<
|
77
|
+
# @param prompt [String] The prompt to generate a chat completion for
|
78
|
+
# @param messages [Array<Hash>] The messages that have been sent in the conversation
|
79
|
+
# @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"
|
80
|
+
# @param examples [Array<Hash>] Examples of messages to provide to the model. Useful for Few-Shot Prompting
|
81
81
|
# @param options [Hash] extra parameters passed to GooglePalmAPI::Client#generate_chat_message
|
82
|
-
# @return [
|
82
|
+
# @return [String] The chat completion
|
83
83
|
#
|
84
84
|
def chat(prompt: "", messages: [], context: "", examples: [], **options)
|
85
85
|
raise ArgumentError.new(":prompt or :messages argument is expected") if prompt.empty? && messages.empty?
|
@@ -87,7 +87,7 @@ module Langchain::LLM
|
|
87
87
|
default_params = {
|
88
88
|
temperature: @defaults[:temperature],
|
89
89
|
model: @defaults[:chat_completion_model_name],
|
90
|
-
context: context
|
90
|
+
context: context,
|
91
91
|
messages: compose_chat_messages(prompt: prompt, messages: messages),
|
92
92
|
examples: compose_examples(examples)
|
93
93
|
}
|
@@ -108,7 +108,7 @@ module Langchain::LLM
|
|
108
108
|
response = client.generate_chat_message(**default_params)
|
109
109
|
raise "GooglePalm API returned an error: #{response}" if response.dig("error")
|
110
110
|
|
111
|
-
|
111
|
+
response.dig("candidates", 0, "content")
|
112
112
|
end
|
113
113
|
|
114
114
|
#
|
@@ -150,8 +150,8 @@ module Langchain::LLM
|
|
150
150
|
def compose_examples(examples)
|
151
151
|
examples.each_slice(2).map do |example|
|
152
152
|
{
|
153
|
-
input: {content: example.first
|
154
|
-
output: {content: example.last
|
153
|
+
input: {content: example.first[:content]},
|
154
|
+
output: {content: example.last[:content]}
|
155
155
|
}
|
156
156
|
end
|
157
157
|
end
|
@@ -159,8 +159,8 @@ module Langchain::LLM
|
|
159
159
|
def transform_messages(messages)
|
160
160
|
messages.map do |message|
|
161
161
|
{
|
162
|
-
author: ROLE_MAPPING.fetch(message
|
163
|
-
content: message
|
162
|
+
author: ROLE_MAPPING.fetch(message[:role], message[:role]),
|
163
|
+
content: message[:content]
|
164
164
|
}
|
165
165
|
end
|
166
166
|
end
|
data/lib/langchain/llm/openai.rb
CHANGED
@@ -11,6 +11,7 @@ module Langchain::LLM
|
|
11
11
|
#
|
12
12
|
class OpenAI < Base
|
13
13
|
DEFAULTS = {
|
14
|
+
n: 1,
|
14
15
|
temperature: 0.0,
|
15
16
|
completion_model_name: "gpt-3.5-turbo",
|
16
17
|
chat_completion_model_name: "gpt-3.5-turbo",
|
@@ -26,10 +27,6 @@ module Langchain::LLM
|
|
26
27
|
].freeze
|
27
28
|
|
28
29
|
LENGTH_VALIDATOR = Langchain::Utils::TokenLength::OpenAIValidator
|
29
|
-
ROLE_MAPPING = {
|
30
|
-
"ai" => "assistant",
|
31
|
-
"human" => "user"
|
32
|
-
}
|
33
30
|
|
34
31
|
attr_accessor :functions
|
35
32
|
|
@@ -117,18 +114,18 @@ module Langchain::LLM
|
|
117
114
|
# },
|
118
115
|
# ]
|
119
116
|
#
|
120
|
-
# @param prompt [
|
121
|
-
# @param messages [Array<
|
122
|
-
# @param context [
|
123
|
-
# @param examples [Array<
|
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
|
124
121
|
# @param options [Hash] extra parameters passed to OpenAI::Client#chat
|
125
|
-
# @yield [
|
126
|
-
# @return [
|
122
|
+
# @yield [Hash] Stream responses back one token at a time
|
123
|
+
# @return [String|Array<String>] The chat completion
|
127
124
|
#
|
128
|
-
def chat(prompt: "", messages: [], context: "", examples: [], **options)
|
125
|
+
def chat(prompt: "", messages: [], context: "", examples: [], **options, &block)
|
129
126
|
raise ArgumentError.new(":prompt or :messages argument is expected") if prompt.empty? && messages.empty?
|
130
127
|
|
131
|
-
parameters = compose_parameters @defaults[:chat_completion_model_name], options
|
128
|
+
parameters = compose_parameters @defaults[:chat_completion_model_name], options, &block
|
132
129
|
parameters[:messages] = compose_chat_messages(prompt: prompt, messages: messages, context: context, examples: examples)
|
133
130
|
|
134
131
|
if functions
|
@@ -137,25 +134,11 @@ module Langchain::LLM
|
|
137
134
|
parameters[:max_tokens] = validate_max_tokens(parameters[:messages], parameters[:model])
|
138
135
|
end
|
139
136
|
|
140
|
-
|
141
|
-
parameters[:stream] = proc do |chunk, _bytesize|
|
142
|
-
delta = chunk.dig("choices", 0, "delta")
|
143
|
-
content = delta["content"]
|
144
|
-
additional_kwargs = {function_call: delta["function_call"]}.compact
|
145
|
-
yield Langchain::AIMessage.new(content, additional_kwargs)
|
146
|
-
end
|
147
|
-
end
|
137
|
+
response = with_api_error_handling { client.chat(parameters: parameters) }
|
148
138
|
|
149
|
-
|
150
|
-
client.chat(parameters: parameters)
|
151
|
-
end
|
139
|
+
return if block
|
152
140
|
|
153
|
-
|
154
|
-
message = response.dig("choices", 0, "message")
|
155
|
-
content = message["content"]
|
156
|
-
additional_kwargs = {function_call: message["function_call"]}.compact
|
157
|
-
Langchain::AIMessage.new(content.to_s, additional_kwargs)
|
158
|
-
end
|
141
|
+
extract_response response
|
159
142
|
end
|
160
143
|
|
161
144
|
#
|
@@ -191,12 +174,18 @@ module Langchain::LLM
|
|
191
174
|
response.dig("choices", 0, "text")
|
192
175
|
end
|
193
176
|
|
194
|
-
def compose_parameters(model, params)
|
195
|
-
default_params = {model: model, temperature: @defaults[:temperature]}
|
196
|
-
|
177
|
+
def compose_parameters(model, params, &block)
|
178
|
+
default_params = {model: model, temperature: @defaults[:temperature], n: @defaults[:n]}
|
197
179
|
default_params[:stop] = params.delete(:stop_sequences) if params[:stop_sequences]
|
180
|
+
parameters = default_params.merge(params)
|
198
181
|
|
199
|
-
|
182
|
+
if block
|
183
|
+
parameters[:stream] = proc do |chunk, _bytesize|
|
184
|
+
yield chunk.dig("choices", 0)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
parameters
|
200
189
|
end
|
201
190
|
|
202
191
|
def compose_chat_messages(prompt:, messages: [], context: "", examples: [])
|
@@ -206,9 +195,9 @@ module Langchain::LLM
|
|
206
195
|
|
207
196
|
history.concat transform_messages(messages) unless messages.empty?
|
208
197
|
|
209
|
-
unless context.nil? || context.
|
198
|
+
unless context.nil? || context.empty?
|
210
199
|
history.reject! { |message| message[:role] == "system" }
|
211
|
-
history.prepend({role: "system", content: context
|
200
|
+
history.prepend({role: "system", content: context})
|
212
201
|
end
|
213
202
|
|
214
203
|
unless prompt.empty?
|
@@ -225,14 +214,16 @@ module Langchain::LLM
|
|
225
214
|
def transform_messages(messages)
|
226
215
|
messages.map do |message|
|
227
216
|
{
|
228
|
-
role:
|
229
|
-
content: message
|
217
|
+
role: message[:role],
|
218
|
+
content: message[:content]
|
230
219
|
}
|
231
220
|
end
|
232
221
|
end
|
233
222
|
|
234
223
|
def with_api_error_handling
|
235
224
|
response = yield
|
225
|
+
return if response.empty?
|
226
|
+
|
236
227
|
raise Langchain::LLM::ApiError.new "OpenAI API error: #{response.dig("error", "message")}" if response&.dig("error")
|
237
228
|
|
238
229
|
response
|
@@ -241,5 +232,10 @@ module Langchain::LLM
|
|
241
232
|
def validate_max_tokens(messages, model)
|
242
233
|
LENGTH_VALIDATOR.validate_max_tokens!(messages, model)
|
243
234
|
end
|
235
|
+
|
236
|
+
def extract_response(response)
|
237
|
+
results = response.dig("choices").map { |choice| choice.dig("message", "content") }
|
238
|
+
(results.size == 1) ? results.first : results
|
239
|
+
end
|
244
240
|
end
|
245
241
|
end
|
@@ -84,7 +84,7 @@ module Langchain::LLM
|
|
84
84
|
# Cohere does not have a dedicated chat endpoint, so instead we call `complete()`
|
85
85
|
def chat(...)
|
86
86
|
response_text = complete(...)
|
87
|
-
Langchain::
|
87
|
+
::Langchain::Conversation::Response.new(response_text)
|
88
88
|
end
|
89
89
|
|
90
90
|
#
|
@@ -5,18 +5,17 @@ module Langchain::Vectorsearch
|
|
5
5
|
#
|
6
6
|
# Wrapper around Chroma DB
|
7
7
|
#
|
8
|
-
# Gem requirements: gem "chroma-db", "~> 0.
|
8
|
+
# Gem requirements: gem "chroma-db", "~> 0.6.0"
|
9
9
|
#
|
10
10
|
# Usage:
|
11
11
|
# chroma = Langchain::Vectorsearch::Chroma.new(url:, index_name:, llm:, llm_api_key:, api_key: nil)
|
12
12
|
#
|
13
13
|
|
14
14
|
# Initialize the Chroma client
|
15
|
-
# @param url [String] The URL of the
|
16
|
-
# @param api_key [String] The API key to use
|
15
|
+
# @param url [String] The URL of the Chroma server
|
17
16
|
# @param index_name [String] The name of the index to use
|
18
17
|
# @param llm [Object] The LLM client to use
|
19
|
-
def initialize(url:, index_name:, llm
|
18
|
+
def initialize(url:, index_name:, llm:)
|
20
19
|
depends_on "chroma-db"
|
21
20
|
|
22
21
|
::Chroma.connect_host = url
|
@@ -61,19 +60,19 @@ module Langchain::Vectorsearch
|
|
61
60
|
end
|
62
61
|
|
63
62
|
# Create the collection with the default schema
|
64
|
-
# @return [
|
63
|
+
# @return [::Chroma::Resources::Collection] Created collection
|
65
64
|
def create_default_schema
|
66
65
|
::Chroma::Resources::Collection.create(index_name)
|
67
66
|
end
|
68
67
|
|
69
68
|
# Get the default schema
|
70
|
-
# @return [
|
69
|
+
# @return [::Chroma::Resources::Collection] Default schema
|
71
70
|
def get_default_schema
|
72
71
|
::Chroma::Resources::Collection.get(index_name)
|
73
72
|
end
|
74
73
|
|
75
74
|
# Delete the default schema
|
76
|
-
# @return [
|
75
|
+
# @return [bool] Success or failure
|
77
76
|
def destroy_default_schema
|
78
77
|
::Chroma::Resources::Collection.delete(index_name)
|
79
78
|
end
|
data/lib/langchain/version.rb
CHANGED
data/lib/langchain.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: langchainrb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.17
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrei Bondarev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-10-
|
11
|
+
date: 2023-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: baran
|
@@ -184,14 +184,14 @@ dependencies:
|
|
184
184
|
requirements:
|
185
185
|
- - "~>"
|
186
186
|
- !ruby/object:Gem::Version
|
187
|
-
version: 0.
|
187
|
+
version: 0.6.0
|
188
188
|
type: :development
|
189
189
|
prerelease: false
|
190
190
|
version_requirements: !ruby/object:Gem::Requirement
|
191
191
|
requirements:
|
192
192
|
- - "~>"
|
193
193
|
- !ruby/object:Gem::Version
|
194
|
-
version: 0.
|
194
|
+
version: 0.6.0
|
195
195
|
- !ruby/object:Gem::Dependency
|
196
196
|
name: cohere-ruby
|
197
197
|
requirement: !ruby/object:Gem::Requirement
|
@@ -492,14 +492,14 @@ dependencies:
|
|
492
492
|
requirements:
|
493
493
|
- - "~>"
|
494
494
|
- !ruby/object:Gem::Version
|
495
|
-
version: 0.8.
|
495
|
+
version: 0.8.9
|
496
496
|
type: :development
|
497
497
|
prerelease: false
|
498
498
|
version_requirements: !ruby/object:Gem::Requirement
|
499
499
|
requirements:
|
500
500
|
- - "~>"
|
501
501
|
- !ruby/object:Gem::Version
|
502
|
-
version: 0.8.
|
502
|
+
version: 0.8.9
|
503
503
|
- !ruby/object:Gem::Dependency
|
504
504
|
name: wikipedia-client
|
505
505
|
requirement: !ruby/object:Gem::Requirement
|
@@ -532,17 +532,21 @@ files:
|
|
532
532
|
- lib/langchain/agent/sql_query_agent.rb
|
533
533
|
- lib/langchain/agent/sql_query_agent/sql_query_agent_answer_prompt.yaml
|
534
534
|
- lib/langchain/agent/sql_query_agent/sql_query_agent_sql_prompt.yaml
|
535
|
-
- lib/langchain/ai_message.rb
|
536
535
|
- lib/langchain/chunker/base.rb
|
536
|
+
- lib/langchain/chunker/prompts/semantic_prompt_template.yml
|
537
537
|
- lib/langchain/chunker/recursive_text.rb
|
538
|
+
- lib/langchain/chunker/semantic.rb
|
538
539
|
- lib/langchain/chunker/sentence.rb
|
539
540
|
- lib/langchain/chunker/text.rb
|
540
541
|
- lib/langchain/contextual_logger.rb
|
541
542
|
- lib/langchain/conversation.rb
|
542
|
-
- lib/langchain/
|
543
|
+
- lib/langchain/conversation/context.rb
|
544
|
+
- lib/langchain/conversation/memory.rb
|
545
|
+
- lib/langchain/conversation/message.rb
|
546
|
+
- lib/langchain/conversation/prompt.rb
|
547
|
+
- lib/langchain/conversation/response.rb
|
543
548
|
- lib/langchain/data.rb
|
544
549
|
- lib/langchain/dependency_helper.rb
|
545
|
-
- lib/langchain/human_message.rb
|
546
550
|
- lib/langchain/llm/ai21.rb
|
547
551
|
- lib/langchain/llm/anthropic.rb
|
548
552
|
- lib/langchain/llm/base.rb
|
@@ -555,7 +559,6 @@ files:
|
|
555
559
|
- lib/langchain/llm/prompts/summarize_template.yaml
|
556
560
|
- lib/langchain/llm/replicate.rb
|
557
561
|
- lib/langchain/loader.rb
|
558
|
-
- lib/langchain/message.rb
|
559
562
|
- lib/langchain/output_parsers/base.rb
|
560
563
|
- lib/langchain/output_parsers/output_fixing_parser.rb
|
561
564
|
- lib/langchain/output_parsers/prompts/naive_fix_prompt.yaml
|
@@ -575,7 +578,6 @@ files:
|
|
575
578
|
- lib/langchain/prompt/loading.rb
|
576
579
|
- lib/langchain/prompt/prompt_template.rb
|
577
580
|
- lib/langchain/railtie.rb
|
578
|
-
- lib/langchain/system_message.rb
|
579
581
|
- lib/langchain/tool/base.rb
|
580
582
|
- lib/langchain/tool/calculator.rb
|
581
583
|
- lib/langchain/tool/database.rb
|
@@ -1,84 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Langchain
|
4
|
-
class ConversationMemory
|
5
|
-
attr_reader :examples, :messages
|
6
|
-
|
7
|
-
# The least number of tokens we want to be under the limit by
|
8
|
-
TOKEN_LEEWAY = 20
|
9
|
-
|
10
|
-
def initialize(llm:, messages: [], **options)
|
11
|
-
@llm = llm
|
12
|
-
@context = nil
|
13
|
-
@summary = nil
|
14
|
-
@examples = []
|
15
|
-
@messages = messages
|
16
|
-
@strategy = options.delete(:strategy) || :truncate
|
17
|
-
@options = options
|
18
|
-
end
|
19
|
-
|
20
|
-
def set_context(message)
|
21
|
-
@context = message
|
22
|
-
end
|
23
|
-
|
24
|
-
def add_examples(examples)
|
25
|
-
@examples.concat examples
|
26
|
-
end
|
27
|
-
|
28
|
-
def append_message(message)
|
29
|
-
@messages.append(message)
|
30
|
-
end
|
31
|
-
|
32
|
-
def reduce_messages(exception)
|
33
|
-
case @strategy
|
34
|
-
when :truncate
|
35
|
-
truncate_messages(exception)
|
36
|
-
when :summarize
|
37
|
-
summarize_messages
|
38
|
-
else
|
39
|
-
raise "Unknown strategy: #{@options[:strategy]}"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def context
|
44
|
-
return if @context.nil? && @summary.nil?
|
45
|
-
|
46
|
-
SystemMessage.new([@context, @summary].compact.join("\n"))
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def truncate_messages(exception)
|
52
|
-
raise exception if @messages.size == 1
|
53
|
-
|
54
|
-
token_overflow = exception.token_overflow
|
55
|
-
|
56
|
-
@messages = @messages.drop_while do |message|
|
57
|
-
proceed = token_overflow > -TOKEN_LEEWAY
|
58
|
-
token_overflow -= token_length(message.to_json, model_name, llm: @llm)
|
59
|
-
|
60
|
-
proceed
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def summarize_messages
|
65
|
-
history = [@summary, @messages.to_json].compact.join("\n")
|
66
|
-
partitions = [history[0, history.size / 2], history[history.size / 2, history.size]]
|
67
|
-
|
68
|
-
@summary = partitions.map { |messages| @llm.summarize(text: messages.to_json) }.join("\n")
|
69
|
-
|
70
|
-
@messages = [@messages.last]
|
71
|
-
end
|
72
|
-
|
73
|
-
def partition_messages
|
74
|
-
end
|
75
|
-
|
76
|
-
def model_name
|
77
|
-
@llm.class::DEFAULTS[:chat_completion_model_name]
|
78
|
-
end
|
79
|
-
|
80
|
-
def token_length(content, model_name, options)
|
81
|
-
@llm.class::LENGTH_VALIDATOR.token_length(content, model_name, options)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
data/lib/langchain/message.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Langchain
|
4
|
-
class Message
|
5
|
-
attr_reader :content, :additional_kwargs
|
6
|
-
|
7
|
-
def initialize(content, additional_kwargs = nil)
|
8
|
-
@content = content
|
9
|
-
@additional_kwargs = additional_kwargs
|
10
|
-
end
|
11
|
-
|
12
|
-
def type
|
13
|
-
raise NotImplementedError
|
14
|
-
end
|
15
|
-
|
16
|
-
def to_s
|
17
|
-
content
|
18
|
-
end
|
19
|
-
|
20
|
-
def ==(other)
|
21
|
-
to_json == other.to_json
|
22
|
-
end
|
23
|
-
|
24
|
-
def to_json(options = {})
|
25
|
-
hash = {
|
26
|
-
type: type,
|
27
|
-
content: content
|
28
|
-
}
|
29
|
-
|
30
|
-
hash[:additional_kwargs] = additional_kwargs unless additional_kwargs.nil? || additional_kwargs.empty?
|
31
|
-
|
32
|
-
hash.to_json
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|