langchainrb 0.5.2 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +5 -3
- data/README.md +15 -7
- data/Rakefile +0 -1
- data/lib/langchain/agent/base.rb +8 -0
- data/lib/langchain/agent/chain_of_thought_agent/chain_of_thought_agent.rb +15 -1
- data/lib/langchain/chat.rb +50 -0
- data/lib/langchain/llm/ai21.rb +9 -9
- data/lib/langchain/llm/base.rb +31 -4
- data/lib/langchain/llm/cohere.rb +10 -9
- data/lib/langchain/llm/google_palm.rb +63 -10
- data/lib/langchain/llm/hugging_face.rb +9 -9
- data/lib/langchain/llm/openai.rb +60 -15
- data/lib/langchain/llm/replicate.rb +16 -15
- data/lib/langchain/processors/base.rb +1 -0
- data/lib/langchain/prompt/base.rb +5 -0
- data/lib/langchain/prompt/few_shot_prompt_template.rb +45 -0
- data/lib/langchain/prompt/prompt_template.rb +31 -0
- data/lib/langchain/tool/base.rb +54 -8
- data/lib/langchain/utils/token_length/google_palm_validator.rb +69 -0
- data/lib/langchain/utils/token_length/openai_validator.rb +75 -0
- data/lib/langchain/vectorsearch/base.rb +82 -0
- data/lib/langchain/vectorsearch/hnswlib.rb +122 -0
- data/lib/langchain/version.rb +1 -1
- data/lib/langchain.rb +51 -1
- metadata +22 -5
- data/lib/langchain/utils/token_length_validator.rb +0 -84
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87647b8a7e2dc49359f3f6d655eda501dcac26ebdd14247ad6c583be8dc1a71c
|
4
|
+
data.tar.gz: fb7b4321caa4ff026439158f5ccfc2ae9e7b515a69c35cba87385f2cb367fa85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f80677c43c00e6d50e0494aa79cb7648b9f3878ed8d2a5f4f2dc90e308a3639589f8457a4615821b70b44c5a43ae4f26fcf00d7548684740e4c05dbcc165bf8
|
7
|
+
data.tar.gz: 4722233dbed83d21f2dadff19a9b79a30d8fd208d6e30bd057f018060c602b7f00f0526ee0364823597806388e2a8da48e883a5e6fa77b31490199685644b4d2
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.5.4] - 2023-06-10
|
4
|
+
- 🔍 Vectorsearch
|
5
|
+
- Introducing support for HNSWlib
|
6
|
+
- Improved and new `Langchain::Chat` interface that persists chat history in memory
|
7
|
+
|
8
|
+
## [0.5.3] - 2023-06-09
|
9
|
+
- 🗣️ LLMs
|
10
|
+
- Chat message history support for Langchain::LLM::GooglePalm and Langchain::LLM::OpenAI
|
11
|
+
|
3
12
|
## [0.5.2] - 2023-06-07
|
4
13
|
- 🗣️ LLMs
|
5
14
|
- Auto-calculate the max_tokens: setting to be passed on to OpenAI
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
langchainrb (0.5.
|
4
|
+
langchainrb (0.5.4)
|
5
5
|
colorize (~> 0.8.1)
|
6
6
|
tiktoken_ruby (~> 0.0.5)
|
7
7
|
|
@@ -122,7 +122,7 @@ GEM
|
|
122
122
|
faraday-retry (1.0.3)
|
123
123
|
faraday_middleware (1.2.0)
|
124
124
|
faraday (~> 1.0)
|
125
|
-
google_palm_api (0.1.
|
125
|
+
google_palm_api (0.1.1)
|
126
126
|
faraday (>= 1.0.0)
|
127
127
|
faraday_middleware (>= 1.0.0)
|
128
128
|
google_search_results (2.0.1)
|
@@ -135,6 +135,7 @@ GEM
|
|
135
135
|
activesupport (>= 3.0)
|
136
136
|
graphql
|
137
137
|
hashery (2.1.2)
|
138
|
+
hnswlib (0.8.1)
|
138
139
|
httparty (0.21.0)
|
139
140
|
mini_mime (>= 1.0.0)
|
140
141
|
multi_xml (>= 0.5.2)
|
@@ -310,8 +311,9 @@ DEPENDENCIES
|
|
310
311
|
docx (~> 0.8.0)
|
311
312
|
dotenv-rails (~> 2.7.6)
|
312
313
|
eqn (~> 1.6.5)
|
313
|
-
google_palm_api (~> 0.1.
|
314
|
+
google_palm_api (~> 0.1.1)
|
314
315
|
google_search_results (~> 2.0.0)
|
316
|
+
hnswlib (~> 0.8.1)
|
315
317
|
hugging-face (~> 0.3.4)
|
316
318
|
langchainrb!
|
317
319
|
milvus (~> 0.9.0)
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
💎🔗 LangChain.rb
|
2
2
|
---
|
3
3
|
⚡ Building applications with LLMs through composability ⚡
|
4
4
|
|
@@ -6,7 +6,10 @@
|
|
6
6
|
|
7
7
|
:warning: UNDER ACTIVE AND RAPID DEVELOPMENT (MAY BE BUGGY AND UNTESTED)
|
8
8
|
|
9
|
-
![Tests status](https://github.com/andreibondarev/langchainrb/actions/workflows/ci.yml/badge.svg)
|
9
|
+
![Tests status](https://github.com/andreibondarev/langchainrb/actions/workflows/ci.yml/badge.svg)
|
10
|
+
[![Gem Version](https://badge.fury.io/rb/langchainrb.svg)](https://badge.fury.io/rb/langchainrb)
|
11
|
+
[![Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/gems/langchainrb)
|
12
|
+
[![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/andreibondarev/langchainrb/blob/main/LICENSE.txt)
|
10
13
|
|
11
14
|
Langchain.rb is a library that's an abstraction layer on top many emergent AI, ML and other DS tools. The goal is to abstract complexity and difficult concepts to make building AI/ML-supercharged applications approachable for traditional software engineers.
|
12
15
|
|
@@ -31,8 +34,10 @@ require "langchain"
|
|
31
34
|
| Database | Querying | Storage | Schema Management | Backups | Rails Integration |
|
32
35
|
| -------- |:------------------:| -------:| -----------------:| -------:| -----------------:|
|
33
36
|
| [Chroma](https://trychroma.com/) | :white_check_mark: | :white_check_mark: | :white_check_mark: | WIP | WIP |
|
37
|
+
| [Hnswlib](https://github.com/nmslib/hnswlib/) | :white_check_mark: | :white_check_mark: | :white_check_mark: | WIP | WIP |
|
34
38
|
| [Milvus](https://milvus.io/) | :white_check_mark: | :white_check_mark: | :white_check_mark: | WIP | WIP |
|
35
39
|
| [Pinecone](https://www.pinecone.io/) | :white_check_mark: | :white_check_mark: | :white_check_mark: | WIP | WIP |
|
40
|
+
| [Pgvector](https://github.com/pgvector/pgvector) | :white_check_mark: | :white_check_mark: | :white_check_mark: | WIP | WIP |
|
36
41
|
| [Qdrant](https://qdrant.tech/) | :white_check_mark: | :white_check_mark: | :white_check_mark: | WIP | WIP |
|
37
42
|
| [Weaviate](https://weaviate.io/) | :white_check_mark: | :white_check_mark: | :white_check_mark: | WIP | WIP |
|
38
43
|
|
@@ -47,14 +52,17 @@ Pick the vector search database you'll be using and instantiate the client:
|
|
47
52
|
client = Langchain::Vectorsearch::Weaviate.new(
|
48
53
|
url: ENV["WEAVIATE_URL"],
|
49
54
|
api_key: ENV["WEAVIATE_API_KEY"],
|
55
|
+
index: "",
|
50
56
|
llm: Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
|
51
57
|
)
|
52
58
|
|
53
59
|
# You can instantiate any other supported vector search database:
|
60
|
+
client = Langchain::Vectorsearch::Chroma.new(...) # `gem "chroma-db", "~> 0.3.0"`
|
61
|
+
client = Langchain::Vectorsearch::Hnswlib.new(...) # `gem "hnswlib", "~> 0.8.1"`
|
54
62
|
client = Langchain::Vectorsearch::Milvus.new(...) # `gem "milvus", "~> 0.9.0"`
|
55
|
-
client = Langchain::Vectorsearch::Qdrant.new(...) # `gem"qdrant-ruby", "~> 0.9.0"`
|
56
63
|
client = Langchain::Vectorsearch::Pinecone.new(...) # `gem "pinecone", "~> 0.1.6"`
|
57
|
-
client = Langchain::Vectorsearch::
|
64
|
+
client = Langchain::Vectorsearch::Pgvector.new(...) # `gem "pgvector", "~> 0.2"`
|
65
|
+
client = Langchain::Vectorsearch::Qdrant.new(...) # `gem"qdrant-ruby", "~> 0.9.0"`
|
58
66
|
```
|
59
67
|
|
60
68
|
```ruby
|
@@ -135,17 +143,17 @@ cohere.complete(prompt: "What is the meaning of life?")
|
|
135
143
|
#### HuggingFace
|
136
144
|
Add `gem "hugging-face", "~> 0.3.2"` to your Gemfile.
|
137
145
|
```ruby
|
138
|
-
|
146
|
+
hugging_face = Langchain::LLM::HuggingFace.new(api_key: ENV["HUGGING_FACE_API_KEY"])
|
139
147
|
```
|
140
148
|
|
141
149
|
#### Replicate
|
142
150
|
Add `gem "replicate-ruby", "~> 0.2.2"` to your Gemfile.
|
143
151
|
```ruby
|
144
|
-
|
152
|
+
replicate = Langchain::LLM::Replicate.new(api_key: ENV["REPLICATE_API_KEY"])
|
145
153
|
```
|
146
154
|
|
147
155
|
#### Google PaLM (Pathways Language Model)
|
148
|
-
Add `"google_palm_api", "~> 0.1.
|
156
|
+
Add `"google_palm_api", "~> 0.1.1"` to your Gemfile.
|
149
157
|
```ruby
|
150
158
|
google_palm = Langchain::LLM::GooglePalm.new(api_key: ENV["GOOGLE_PALM_API_KEY"])
|
151
159
|
```
|
data/Rakefile
CHANGED
data/lib/langchain/agent/base.rb
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Langchain::Agent
|
4
|
+
# = Agents
|
5
|
+
#
|
6
|
+
# Agents are semi-autonomous bots that can respond to user questions and use available to them Tools to provide informed replies. They break down problems into series of steps and define Actions (and Action Inputs) along the way that are executed and fed back to them as additional information. Once an Agent decides that it has the Final Answer it responds with it.
|
7
|
+
#
|
8
|
+
# Available:
|
9
|
+
# - {Langchain::Agent::ChainOfThoughtAgent}
|
10
|
+
#
|
11
|
+
# @abstract
|
4
12
|
class Base
|
5
13
|
end
|
6
14
|
end
|
@@ -1,6 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Langchain::Agent
|
4
|
+
# = Chain of Thought Agent
|
5
|
+
#
|
6
|
+
# llm = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"]) # or your choice of Langchain::LLM::Base implementation
|
7
|
+
#
|
8
|
+
# agent = Langchain::Agent::ChainOfThoughtAgent.new(
|
9
|
+
# llm: llm,
|
10
|
+
# tools: ["search", "calculator", "wikipedia"]
|
11
|
+
# )
|
12
|
+
#
|
13
|
+
# agent.tools
|
14
|
+
# # => ["search", "calculator", "wikipedia"]
|
15
|
+
#
|
16
|
+
# agent.run(question: "How many full soccer fields would be needed to cover the distance between NYC and DC in a straight line?")
|
17
|
+
# #=> "Approximately 2,945 soccer fields would be needed to cover the distance between NYC and DC in a straight line."
|
4
18
|
class ChainOfThoughtAgent < Base
|
5
19
|
attr_reader :llm, :tools
|
6
20
|
|
@@ -87,7 +101,7 @@ module Langchain::Agent
|
|
87
101
|
tool_names: "[#{tool_list.join(", ")}]",
|
88
102
|
tools: tools.map do |tool|
|
89
103
|
tool_name = tool.tool_name
|
90
|
-
tool_description = tool.
|
104
|
+
tool_description = tool.tool_description
|
91
105
|
"#{tool_name}: #{tool_description}"
|
92
106
|
end.join("\n")
|
93
107
|
)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Langchain
|
4
|
+
class Chat
|
5
|
+
attr_reader :context
|
6
|
+
|
7
|
+
def initialize(llm:, **options)
|
8
|
+
@llm = llm
|
9
|
+
@context = nil
|
10
|
+
@examples = []
|
11
|
+
@messages = []
|
12
|
+
end
|
13
|
+
|
14
|
+
# Set the context of the conversation. Usually used to set the model's persona.
|
15
|
+
# @param message [String] The context of the conversation
|
16
|
+
def set_context(message)
|
17
|
+
@context = message
|
18
|
+
end
|
19
|
+
|
20
|
+
# Add examples to the conversation. Used to give the model a sense of the conversation.
|
21
|
+
# @param examples [Array<Hash>] The examples to add to the conversation
|
22
|
+
def add_examples(examples)
|
23
|
+
@examples.concat examples
|
24
|
+
end
|
25
|
+
|
26
|
+
# Message the model with a prompt and return the response.
|
27
|
+
# @param message [String] The prompt to message the model with
|
28
|
+
# @return [String] The response from the model
|
29
|
+
def message(message)
|
30
|
+
append_user_message(message)
|
31
|
+
response = llm_response(message)
|
32
|
+
append_ai_message(response)
|
33
|
+
response
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def llm_response(prompt)
|
39
|
+
@llm.chat(messages: @messages, context: @context, examples: @examples)
|
40
|
+
end
|
41
|
+
|
42
|
+
def append_ai_message(message)
|
43
|
+
@messages << {role: "ai", content: message}
|
44
|
+
end
|
45
|
+
|
46
|
+
def append_user_message(message)
|
47
|
+
@messages << {role: "user", content: message}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/langchain/llm/ai21.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Langchain::LLM
|
4
|
+
#
|
5
|
+
# Wrapper around AI21 Studio APIs.
|
6
|
+
#
|
7
|
+
# Gem requirements:
|
8
|
+
# gem "ai21", "~> 0.2.0"
|
9
|
+
#
|
10
|
+
# Usage:
|
11
|
+
# ai21 = Langchain::LLM::AI21.new(api_key:)
|
12
|
+
#
|
4
13
|
class AI21 < Base
|
5
|
-
#
|
6
|
-
# Wrapper around AI21 Studio APIs.
|
7
|
-
#
|
8
|
-
# Gem requirements: gem "ai21", "~> 0.2.0"
|
9
|
-
#
|
10
|
-
# Usage:
|
11
|
-
# ai21 = Langchain::LLM::AI21.new(api_key:)
|
12
|
-
#
|
13
|
-
|
14
14
|
def initialize(api_key:)
|
15
15
|
depends_on "ai21"
|
16
16
|
require "ai21"
|
data/lib/langchain/llm/base.rb
CHANGED
@@ -1,31 +1,58 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Langchain::LLM
|
4
|
+
# A LLM is a language model consisting of a neural network with many parameters (typically billions of weights or more), trained on large quantities of unlabeled text using self-supervised learning or semi-supervised learning.
|
5
|
+
#
|
6
|
+
# Langchain.rb provides a common interface to interact with all supported LLMs:
|
7
|
+
#
|
8
|
+
# - {Langchain::LLM::AI21}
|
9
|
+
# - {Langchain::LLM::Cohere}
|
10
|
+
# - {Langchain::LLM::GooglePalm}
|
11
|
+
# - {Langchain::LLM::HuggingFace}
|
12
|
+
# - {Langchain::LLM::OpenAI}
|
13
|
+
# - {Langchain::LLM::Replicate}
|
14
|
+
#
|
15
|
+
# @abstract
|
4
16
|
class Base
|
5
17
|
include Langchain::DependencyHelper
|
6
18
|
|
19
|
+
# A client for communicating with the LLM
|
7
20
|
attr_reader :client
|
8
21
|
|
9
22
|
def default_dimension
|
10
23
|
self.class.const_get(:DEFAULTS).dig(:dimension)
|
11
24
|
end
|
12
25
|
|
13
|
-
#
|
26
|
+
#
|
27
|
+
# Generate a chat completion for a given prompt. Parameters will depend on the LLM
|
28
|
+
#
|
29
|
+
# @raise NotImplementedError if not supported by the LLM
|
14
30
|
def chat(...)
|
15
31
|
raise NotImplementedError, "#{self.class.name} does not support chat"
|
16
32
|
end
|
17
33
|
|
18
|
-
#
|
34
|
+
#
|
35
|
+
# Generate a completion for a given prompt. Parameters will depend on the LLM.
|
36
|
+
#
|
37
|
+
# @raise NotImplementedError if not supported by the LLM
|
19
38
|
def complete(...)
|
20
39
|
raise NotImplementedError, "#{self.class.name} does not support completion"
|
21
40
|
end
|
22
41
|
|
23
|
-
#
|
42
|
+
#
|
43
|
+
# Generate an embedding for a given text. Parameters depends on the LLM.
|
44
|
+
#
|
45
|
+
# @raise NotImplementedError if not supported by the LLM
|
46
|
+
#
|
24
47
|
def embed(...)
|
25
48
|
raise NotImplementedError, "#{self.class.name} does not support generating embeddings"
|
26
49
|
end
|
27
50
|
|
28
|
-
#
|
51
|
+
#
|
52
|
+
# Generate a summary for a given text. Parameters depends on the LLM.
|
53
|
+
#
|
54
|
+
# @raise NotImplementedError if not supported by the LLM
|
55
|
+
#
|
29
56
|
def summarize(...)
|
30
57
|
raise NotImplementedError, "#{self.class.name} does not support summarization"
|
31
58
|
end
|
data/lib/langchain/llm/cohere.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Langchain::LLM
|
4
|
+
#
|
5
|
+
# Wrapper around the Cohere API.
|
6
|
+
#
|
7
|
+
# Gem requirements:
|
8
|
+
# gem "cohere-ruby", "~> 0.9.4"
|
9
|
+
#
|
10
|
+
# Usage:
|
11
|
+
# cohere = Langchain::LLM::Cohere.new(api_key: "YOUR_API_KEY")
|
12
|
+
#
|
4
13
|
class Cohere < Base
|
5
|
-
#
|
6
|
-
# Wrapper around the Cohere API.
|
7
|
-
#
|
8
|
-
# Gem requirements: gem "cohere-ruby", "~> 0.9.4"
|
9
|
-
#
|
10
|
-
# Usage:
|
11
|
-
# cohere = Langchain::LLM::Cohere.new(api_key: "YOUR_API_KEY")
|
12
|
-
#
|
13
|
-
|
14
14
|
DEFAULTS = {
|
15
15
|
temperature: 0.0,
|
16
16
|
completion_model_name: "base",
|
@@ -43,6 +43,7 @@ module Langchain::LLM
|
|
43
43
|
# Generate a completion for a given prompt
|
44
44
|
#
|
45
45
|
# @param prompt [String] The prompt to generate a completion for
|
46
|
+
# @param params[:stop_sequences]
|
46
47
|
# @return [Hash] The completion
|
47
48
|
#
|
48
49
|
def complete(prompt:, **params)
|
@@ -1,11 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Langchain::LLM
|
4
|
+
#
|
5
|
+
# Wrapper around the Google PaLM (Pathways Language Model) APIs: https://ai.google/build/machine-learning/
|
6
|
+
#
|
7
|
+
# Gem requirements:
|
8
|
+
# gem "google_palm_api", "~> 0.1.0"
|
9
|
+
#
|
10
|
+
# Usage:
|
11
|
+
# google_palm = Langchain::LLM::GooglePalm.new(api_key: "YOUR_API_KEY")
|
12
|
+
#
|
4
13
|
class GooglePalm < Base
|
5
14
|
#
|
6
15
|
# Wrapper around the Google PaLM (Pathways Language Model) APIs.
|
7
16
|
#
|
8
|
-
# Gem requirements: gem "google_palm_api", "~> 0.1.
|
17
|
+
# Gem requirements: gem "google_palm_api", "~> 0.1.1"
|
9
18
|
#
|
10
19
|
# Usage:
|
11
20
|
# google_palm = Langchain::LLM::GooglePalm.new(api_key: "YOUR_API_KEY")
|
@@ -40,6 +49,7 @@ module Langchain::LLM
|
|
40
49
|
# Generate a completion for a given prompt
|
41
50
|
#
|
42
51
|
# @param prompt [String] The prompt to generate a completion for
|
52
|
+
# @param params extra parameters passed to GooglePalmAPI::Client#generate_text
|
43
53
|
# @return [String] The completion
|
44
54
|
#
|
45
55
|
def complete(prompt:, **params)
|
@@ -66,26 +76,35 @@ module Langchain::LLM
|
|
66
76
|
# Generate a chat completion for a given prompt
|
67
77
|
#
|
68
78
|
# @param prompt [String] The prompt to generate a chat completion for
|
79
|
+
# @param messages [Array] The messages that have been sent in the conversation
|
80
|
+
# @param params extra parameters passed to GooglePalmAPI::Client#generate_chat_message
|
69
81
|
# @return [String] The chat completion
|
70
82
|
#
|
71
|
-
def chat(prompt
|
72
|
-
|
83
|
+
def chat(prompt: "", messages: [], context: "", examples: [], **options)
|
84
|
+
raise ArgumentError.new(":prompt or :messages argument is expected") if prompt.empty? && messages.empty?
|
85
|
+
|
73
86
|
default_params = {
|
74
|
-
|
75
|
-
|
87
|
+
temperature: DEFAULTS[:temperature],
|
88
|
+
context: context,
|
89
|
+
messages: compose_chat_messages(prompt: prompt, messages: messages),
|
90
|
+
examples: compose_examples(examples)
|
76
91
|
}
|
77
92
|
|
78
|
-
|
79
|
-
|
93
|
+
Langchain::Utils::TokenLength::GooglePalmValidator.validate_max_tokens!(self, default_params[:messages], "chat-bison-001")
|
94
|
+
|
95
|
+
if options[:stop_sequences]
|
96
|
+
default_params[:stop] = options.delete(:stop_sequences)
|
80
97
|
end
|
81
98
|
|
82
|
-
if
|
83
|
-
default_params[:max_output_tokens] =
|
99
|
+
if options[:max_tokens]
|
100
|
+
default_params[:max_output_tokens] = options.delete(:max_tokens)
|
84
101
|
end
|
85
102
|
|
86
|
-
default_params.merge!(
|
103
|
+
default_params.merge!(options)
|
87
104
|
|
88
105
|
response = client.generate_chat_message(**default_params)
|
106
|
+
raise "GooglePalm API returned an error: #{response}" if response.dig("error")
|
107
|
+
|
89
108
|
response.dig("candidates", 0, "content")
|
90
109
|
end
|
91
110
|
|
@@ -108,5 +127,39 @@ module Langchain::LLM
|
|
108
127
|
max_tokens: 2048
|
109
128
|
)
|
110
129
|
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def compose_chat_messages(prompt:, messages:)
|
134
|
+
history = []
|
135
|
+
history.concat transform_messages(messages) unless messages.empty?
|
136
|
+
|
137
|
+
unless prompt.empty?
|
138
|
+
if history.last && history.last[:role] == "user"
|
139
|
+
history.last[:content] += "\n#{prompt}"
|
140
|
+
else
|
141
|
+
history.append({author: "user", content: prompt})
|
142
|
+
end
|
143
|
+
end
|
144
|
+
history
|
145
|
+
end
|
146
|
+
|
147
|
+
def compose_examples(examples)
|
148
|
+
examples.each_slice(2).map do |example|
|
149
|
+
{
|
150
|
+
input: {content: example.first[:content]},
|
151
|
+
output: {content: example.last[:content]}
|
152
|
+
}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def transform_messages(messages)
|
157
|
+
messages.map do |message|
|
158
|
+
{
|
159
|
+
author: message[:role],
|
160
|
+
content: message[:content]
|
161
|
+
}
|
162
|
+
end
|
163
|
+
end
|
111
164
|
end
|
112
165
|
end
|
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Langchain::LLM
|
4
|
+
#
|
5
|
+
# Wrapper around the HuggingFace Inference API: https://huggingface.co/inference-api
|
6
|
+
#
|
7
|
+
# Gem requirements:
|
8
|
+
# gem "hugging-face", "~> 0.3.4"
|
9
|
+
#
|
10
|
+
# Usage:
|
11
|
+
# hf = Langchain::LLM::HuggingFace.new(api_key: "YOUR_API_KEY")
|
12
|
+
#
|
4
13
|
class HuggingFace < Base
|
5
|
-
#
|
6
|
-
# Wrapper around the HuggingFace Inference API.
|
7
|
-
#
|
8
|
-
# Gem requirements: gem "hugging-face", "~> 0.3.4"
|
9
|
-
#
|
10
|
-
# Usage:
|
11
|
-
# hf = Langchain::LLM::HuggingFace.new(api_key: "YOUR_API_KEY")
|
12
|
-
#
|
13
|
-
|
14
14
|
# The gem does not currently accept other models:
|
15
15
|
# https://github.com/alchaplinsky/hugging-face/blob/main/lib/hugging_face/inference_api.rb#L32-L34
|
16
16
|
DEFAULTS = {
|
data/lib/langchain/llm/openai.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Langchain::LLM
|
4
|
+
# LLM interface for OpenAI APIs: https://platform.openai.com/overview
|
5
|
+
#
|
6
|
+
# Gem requirements:
|
7
|
+
# gem "ruby-openai", "~> 4.0.0"
|
8
|
+
#
|
9
|
+
# Usage:
|
10
|
+
# openai = Langchain::LLM::OpenAI.new(api_key:, llm_options: {})
|
11
|
+
#
|
4
12
|
class OpenAI < Base
|
5
|
-
#
|
6
|
-
# Wrapper around OpenAI APIs.
|
7
|
-
#
|
8
|
-
# Gem requirements: gem "ruby-openai", "~> 4.0.0"
|
9
|
-
#
|
10
|
-
# Usage:
|
11
|
-
# openai = Langchain::LLM::OpenAI.new(api_key:, llm_options: {})
|
12
|
-
#
|
13
|
-
|
14
13
|
DEFAULTS = {
|
15
14
|
temperature: 0.0,
|
16
15
|
completion_model_name: "text-davinci-003",
|
@@ -30,12 +29,13 @@ module Langchain::LLM
|
|
30
29
|
# Generate an embedding for a given text
|
31
30
|
#
|
32
31
|
# @param text [String] The text to generate an embedding for
|
32
|
+
# @param params extra parameters passed to OpenAI::Client#embeddings
|
33
33
|
# @return [Array] The embedding
|
34
34
|
#
|
35
35
|
def embed(text:, **params)
|
36
36
|
parameters = {model: DEFAULTS[:embeddings_model_name], input: text}
|
37
37
|
|
38
|
-
Langchain::Utils::
|
38
|
+
Langchain::Utils::TokenLength::OpenAIValidator.validate_max_tokens!(text, parameters[:model])
|
39
39
|
|
40
40
|
response = client.embeddings(parameters: parameters.merge(params))
|
41
41
|
response.dig("data").first.dig("embedding")
|
@@ -45,13 +45,14 @@ module Langchain::LLM
|
|
45
45
|
# Generate a completion for a given prompt
|
46
46
|
#
|
47
47
|
# @param prompt [String] The prompt to generate a completion for
|
48
|
+
# @param params extra parameters passed to OpenAI::Client#complete
|
48
49
|
# @return [String] The completion
|
49
50
|
#
|
50
51
|
def complete(prompt:, **params)
|
51
52
|
parameters = compose_parameters DEFAULTS[:completion_model_name], params
|
52
53
|
|
53
54
|
parameters[:prompt] = prompt
|
54
|
-
parameters[:max_tokens] = Langchain::Utils::
|
55
|
+
parameters[:max_tokens] = Langchain::Utils::TokenLength::OpenAIValidator.validate_max_tokens!(prompt, parameters[:model])
|
55
56
|
|
56
57
|
response = client.completions(parameters: parameters)
|
57
58
|
response.dig("choices", 0, "text")
|
@@ -61,15 +62,23 @@ module Langchain::LLM
|
|
61
62
|
# Generate a chat completion for a given prompt
|
62
63
|
#
|
63
64
|
# @param prompt [String] The prompt to generate a chat completion for
|
65
|
+
# @param messages [Array] The messages that have been sent in the conversation
|
66
|
+
# @param context [String] The context of the conversation
|
67
|
+
# @param examples [Array] Examples of messages provide model with
|
68
|
+
# @param options extra parameters passed to OpenAI::Client#chat
|
64
69
|
# @return [String] The chat completion
|
65
70
|
#
|
66
|
-
def chat(prompt
|
67
|
-
|
71
|
+
def chat(prompt: "", messages: [], context: "", examples: [], **options)
|
72
|
+
raise ArgumentError.new(":prompt or :messages argument is expected") if prompt.empty? && messages.empty?
|
68
73
|
|
69
|
-
parameters
|
70
|
-
parameters[:
|
74
|
+
parameters = compose_parameters DEFAULTS[:chat_completion_model_name], options
|
75
|
+
parameters[:messages] = compose_chat_messages(prompt: prompt, messages: messages, context: context, examples: examples)
|
76
|
+
parameters[:max_tokens] = validate_max_tokens(parameters[:messages], parameters[:model])
|
71
77
|
|
72
78
|
response = client.chat(parameters: parameters)
|
79
|
+
|
80
|
+
raise "Chat completion failed: #{response}" if response.dig("error")
|
81
|
+
|
73
82
|
response.dig("choices", 0, "message", "content")
|
74
83
|
end
|
75
84
|
|
@@ -97,5 +106,41 @@ module Langchain::LLM
|
|
97
106
|
|
98
107
|
default_params.merge(params)
|
99
108
|
end
|
109
|
+
|
110
|
+
def compose_chat_messages(prompt:, messages:, context:, examples:)
|
111
|
+
history = []
|
112
|
+
|
113
|
+
history.concat transform_messages(examples) unless examples.empty?
|
114
|
+
|
115
|
+
history.concat transform_messages(messages) unless messages.empty?
|
116
|
+
|
117
|
+
unless context.nil? || context.empty?
|
118
|
+
history.reject! { |message| message[:role] == "system" }
|
119
|
+
history.prepend({role: "system", content: context})
|
120
|
+
end
|
121
|
+
|
122
|
+
unless prompt.empty?
|
123
|
+
if history.last && history.last[:role] == "user"
|
124
|
+
history.last[:content] += "\n#{prompt}"
|
125
|
+
else
|
126
|
+
history.append({role: "user", content: prompt})
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
history
|
131
|
+
end
|
132
|
+
|
133
|
+
def transform_messages(messages)
|
134
|
+
messages.map do |message|
|
135
|
+
{
|
136
|
+
content: message[:content],
|
137
|
+
role: (message[:role] == "ai") ? "assistant" : message[:role]
|
138
|
+
}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def validate_max_tokens(messages, model)
|
143
|
+
Langchain::Utils::TokenLength::OpenAIValidator.validate_max_tokens!(messages, model)
|
144
|
+
end
|
100
145
|
end
|
101
146
|
end
|
@@ -1,22 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Langchain::LLM
|
4
|
+
#
|
5
|
+
# Wrapper around Replicate.com LLM provider
|
6
|
+
#
|
7
|
+
# Gem requirements:
|
8
|
+
# gem "replicate-ruby", "~> 0.2.2"
|
9
|
+
#
|
10
|
+
# Use it directly:
|
11
|
+
# replicate = Langchain::LLM::Replicate.new(api_key: ENV["REPLICATE_API_KEY"])
|
12
|
+
#
|
13
|
+
# Or pass it to be used by a vector search DB:
|
14
|
+
# chroma = Langchain::Vectorsearch::Chroma.new(
|
15
|
+
# url: ENV["CHROMA_URL"],
|
16
|
+
# index_name: "...",
|
17
|
+
# llm: replicate
|
18
|
+
# )
|
19
|
+
#
|
4
20
|
class Replicate < Base
|
5
|
-
#
|
6
|
-
# Wrapper around Replicate.com LLM provider
|
7
|
-
#
|
8
|
-
# Gem requirements: gem "replicate-ruby", "~> 0.2.2"
|
9
|
-
#
|
10
|
-
# Use it directly:
|
11
|
-
# replicate = LLM::Replicate.new(api_key: ENV["REPLICATE_API_KEY"])
|
12
|
-
#
|
13
|
-
# Or pass it to be instantiated by a vector search DB:
|
14
|
-
# chroma = Vectorsearch::Chroma.new(
|
15
|
-
# url: ENV["CHROMA_URL"],
|
16
|
-
# index_name: "...",
|
17
|
-
# llm: Langchain::LLM::Replicate(api_key: ENV["REPLICATE_API_KEY"])
|
18
|
-
# )
|
19
|
-
|
20
21
|
DEFAULTS = {
|
21
22
|
# TODO: Figure out how to send the temperature to the API
|
22
23
|
temperature: 0.01, # Minimum accepted value
|