langchainrb 0.5.6 → 0.5.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 045a900e70f73ac0c969ea0e7cb0130d12219ad869583a55d5f5857ceccac618
4
- data.tar.gz: f6202d34280eeda69026add6cb0bcadb1625da58ed729e1b4ca02c2cfdbd76b4
3
+ metadata.gz: ba5e9e8257d18c0940fdaf4fe84c03d594d8f1151e40e1bb35de059f8e6e5094
4
+ data.tar.gz: 11310635819502b9bfbd66bc45dc7aa1ce500d4a874dcc5ab550d6c5edf7194f
5
5
  SHA512:
6
- metadata.gz: b5cd3983b8a7389baace3befd24751d1c2974b94da29868fac6bfcd048681d2b6cc603d13f791d7ca4bffbc18b9278704c3db188112b51f1c71ac528c6c04f70
7
- data.tar.gz: c061c1a877bc94488177ef79a46ed558540ba664a001a463a95fbe7f1f5f50c8895f359ab06fce26bb7dedf8cd246713b96e553fdfa55ca5b68c78f124e87a2a
6
+ metadata.gz: 4b97e21bcbc0c5f1d842271b64949c07d6d78190cd97c22fd0dab735d6b6ae2f2e6328ba2631dfc77ed0a5dd227573e3f84f064e8dd9332701848a798747ac9a
7
+ data.tar.gz: 267b2029de10acf45bb97a040d174102f666e048aaaf03ab76218cd5281574c1ae977ba8e975faf4b690e677611daba2fb0fc975801c0e41072f050ec2ac2e34
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.7] - 2023-06-19
4
+ - Developer can modify models used when initiliazing `Langchain::LLM::*` clients
5
+ - Improvements to the `SQLQueryAgent` and the database tool
6
+
3
7
  ## [0.5.6] - 2023-06-18
4
8
  - If used with OpenAI, Langchain::Conversation responses can now be streamed.
5
9
  - Improved logging
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- langchainrb (0.5.6)
4
+ langchainrb (0.5.7)
5
5
  baran (~> 0.1.6)
6
6
  colorize (~> 0.8.1)
7
7
  tiktoken_ruby (~> 0.0.5)
data/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  [![Gem Version](https://badge.fury.io/rb/langchainrb.svg)](https://badge.fury.io/rb/langchainrb)
11
11
  [![Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/gems/langchainrb)
12
12
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/andreibondarev/langchainrb/blob/main/LICENSE.txt)
13
- [![](https://dcbadge.vercel.app/api/server/WWqjwxMv?compact=true&style=flat)](https://discord.gg/WWqjwxMv)
13
+ [![](https://dcbadge.vercel.app/api/server/WDARp7J2n8?compact=true&style=flat)](https://discord.gg/WDARp7J2n8)
14
14
 
15
15
 
16
16
  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.
@@ -367,7 +367,7 @@ Langchain.logger.level = :info
367
367
  5. Optionally, install lefthook git hooks for pre-commit to auto lint: `gem install lefthook && lefthook install -f`
368
368
 
369
369
  ## Discord
370
- Join us in the [Langchain.rb](https://discord.gg/hXutDWGDd) Discord server.
370
+ Join us in the [Langchain.rb](https://discord.gg/WDARp7J2n8) Discord server.
371
371
 
372
372
  ## Core Contributors
373
373
  [<img style="border-radius:50%" alt="Andrei Bondarev" src="https://avatars.githubusercontent.com/u/541665?v=4" width="80" height="80" class="avatar">](https://github.com/andreibondarev)
@@ -13,7 +13,7 @@ module Langchain::Agent
13
13
  def initialize(llm:, db:)
14
14
  @llm = llm
15
15
  @db = db
16
- @schema = @db.schema
16
+ @schema = @db.dump_schema
17
17
  end
18
18
 
19
19
  #
@@ -11,11 +11,17 @@ module Langchain::LLM
11
11
  # ai21 = Langchain::LLM::AI21.new(api_key:)
12
12
  #
13
13
  class AI21 < Base
14
- def initialize(api_key:)
14
+ DEFAULTS = {
15
+ temperature: 0.0,
16
+ model: "j2-large"
17
+ }.freeze
18
+
19
+ def initialize(api_key:, default_options: {})
15
20
  depends_on "ai21"
16
21
  require "ai21"
17
22
 
18
23
  @client = ::AI21::Client.new(api_key)
24
+ @defaults = DEFAULTS.merge(default_options)
19
25
  end
20
26
 
21
27
  #
@@ -26,7 +32,9 @@ module Langchain::LLM
26
32
  # @return [String] The completion
27
33
  #
28
34
  def complete(prompt:, **params)
29
- response = client.complete(prompt, params)
35
+ parameters = complete_parameters params
36
+
37
+ response = client.complete(prompt, parameters)
30
38
  response.dig(:completions, 0, :data, :text)
31
39
  end
32
40
 
@@ -41,5 +49,11 @@ module Langchain::LLM
41
49
  response = client.summarize(text, "TEXT", params)
42
50
  response.dig(:summary)
43
51
  end
52
+
53
+ private
54
+
55
+ def complete_parameters(params)
56
+ @defaults.dup.merge(params)
57
+ end
44
58
  end
45
59
  end
@@ -18,11 +18,12 @@ module Langchain::LLM
18
18
  dimension: 1024
19
19
  }.freeze
20
20
 
21
- def initialize(api_key:)
21
+ def initialize(api_key:, default_options: {})
22
22
  depends_on "cohere-ruby"
23
23
  require "cohere"
24
24
 
25
25
  @client = ::Cohere::Client.new(api_key: api_key)
26
+ @defaults = DEFAULTS.merge(default_options)
26
27
  end
27
28
 
28
29
  #
@@ -34,7 +35,7 @@ module Langchain::LLM
34
35
  def embed(text:)
35
36
  response = client.embed(
36
37
  texts: [text],
37
- model: DEFAULTS[:embeddings_model_name]
38
+ model: @defaults[:embeddings_model_name]
38
39
  )
39
40
  response.dig("embeddings").first
40
41
  end
@@ -49,8 +50,8 @@ module Langchain::LLM
49
50
  def complete(prompt:, **params)
50
51
  default_params = {
51
52
  prompt: prompt,
52
- temperature: DEFAULTS[:temperature],
53
- model: DEFAULTS[:completion_model_name]
53
+ temperature: @defaults[:temperature],
54
+ model: @defaults[:completion_model_name]
54
55
  }
55
56
 
56
57
  if params[:stop_sequences]
@@ -22,15 +22,19 @@ module Langchain::LLM
22
22
 
23
23
  DEFAULTS = {
24
24
  temperature: 0.0,
25
- dimension: 768 # This is what the `embedding-gecko-001` model generates
25
+ dimension: 768, # This is what the `embedding-gecko-001` model generates
26
+ completion_model_name: "text-bison-001",
27
+ chat_completion_model_name: "chat-bison-001",
28
+ embeddings_model_name: "embedding-gecko-001"
26
29
  }.freeze
27
30
  LENGTH_VALIDATOR = Langchain::Utils::TokenLength::GooglePalmValidator
28
31
 
29
- def initialize(api_key:)
32
+ def initialize(api_key:, default_options: {})
30
33
  depends_on "google_palm_api"
31
34
  require "google_palm_api"
32
35
 
33
36
  @client = ::GooglePalmApi::Client.new(api_key: api_key)
37
+ @defaults = DEFAULTS.merge(default_options)
34
38
  end
35
39
 
36
40
  #
@@ -56,7 +60,8 @@ module Langchain::LLM
56
60
  def complete(prompt:, **params)
57
61
  default_params = {
58
62
  prompt: prompt,
59
- temperature: DEFAULTS[:temperature]
63
+ temperature: @defaults[:temperature],
64
+ completion_model_name: @defaults[:completion_model_name]
60
65
  }
61
66
 
62
67
  if params[:stop_sequences]
@@ -85,12 +90,14 @@ module Langchain::LLM
85
90
  raise ArgumentError.new(":prompt or :messages argument is expected") if prompt.empty? && messages.empty?
86
91
 
87
92
  default_params = {
88
- temperature: DEFAULTS[:temperature],
93
+ temperature: @defaults[:temperature],
94
+ chat_completion_model_name: @defaults[:chat_completion_model_name],
89
95
  context: context,
90
96
  messages: compose_chat_messages(prompt: prompt, messages: messages),
91
97
  examples: compose_examples(examples)
92
98
  }
93
99
 
100
+ # chat-bison-001 is the only model that currently supports countMessageTokens functions
94
101
  LENGTH_VALIDATOR.validate_max_tokens!(default_params[:messages], "chat-bison-001", llm: self)
95
102
 
96
103
  if options[:stop_sequences]
@@ -123,7 +130,7 @@ module Langchain::LLM
123
130
 
124
131
  complete(
125
132
  prompt: prompt,
126
- temperature: DEFAULTS[:temperature],
133
+ temperature: @defaults[:temperature],
127
134
  # Most models have a context length of 2048 tokens (except for the newest models, which support 4096).
128
135
  max_tokens: 2048
129
136
  )
@@ -19,11 +19,12 @@ module Langchain::LLM
19
19
  }.freeze
20
20
  LENGTH_VALIDATOR = Langchain::Utils::TokenLength::OpenAIValidator
21
21
 
22
- def initialize(api_key:, llm_options: {})
22
+ def initialize(api_key:, llm_options: {}, default_options: {})
23
23
  depends_on "ruby-openai"
24
24
  require "openai"
25
25
 
26
26
  @client = ::OpenAI::Client.new(access_token: api_key, **llm_options)
27
+ @defaults = DEFAULTS.merge(default_options)
27
28
  end
28
29
 
29
30
  #
@@ -34,7 +35,7 @@ module Langchain::LLM
34
35
  # @return [Array] The embedding
35
36
  #
36
37
  def embed(text:, **params)
37
- parameters = {model: DEFAULTS[:embeddings_model_name], input: text}
38
+ parameters = {model: @defaults[:embeddings_model_name], input: text}
38
39
 
39
40
  validate_max_tokens(text, parameters[:model])
40
41
 
@@ -50,7 +51,7 @@ module Langchain::LLM
50
51
  # @return [String] The completion
51
52
  #
52
53
  def complete(prompt:, **params)
53
- parameters = compose_parameters DEFAULTS[:completion_model_name], params
54
+ parameters = compose_parameters @defaults[:completion_model_name], params
54
55
 
55
56
  parameters[:prompt] = prompt
56
57
  parameters[:max_tokens] = validate_max_tokens(prompt, parameters[:model])
@@ -60,20 +61,59 @@ module Langchain::LLM
60
61
  end
61
62
 
62
63
  #
63
- # Generate a chat completion for a given prompt
64
+ # Generate a chat completion for a given prompt or messages.
65
+ #
66
+ # == Examples
67
+ #
68
+ # # simplest case, just give a prompt
69
+ # openai.chat prompt: "When was Ruby first released?"
70
+ #
71
+ # # prompt plus some context about how to respond
72
+ # openai.chat context: "You are RubyGPT, a helpful chat bot for helping people learn Ruby", prompt: "Does Ruby have a REPL like IPython?"
73
+ #
74
+ # # full control over messages that get sent, equivilent to the above
75
+ # openai.chat messages: [
76
+ # {
77
+ # role: "system",
78
+ # content: "You are RubyGPT, a helpful chat bot for helping people learn Ruby", prompt: "Does Ruby have a REPL like IPython?"
79
+ # },
80
+ # {
81
+ # role: "user",
82
+ # content: "When was Ruby first released?"
83
+ # }
84
+ # ]
85
+ #
86
+ # # few-short prompting with examples
87
+ # openai.chat prompt: "When was factory_bot released?",
88
+ # examples: [
89
+ # {
90
+ # role: "user",
91
+ # content: "When was Ruby on Rails released?"
92
+ # }
93
+ # {
94
+ # role: "assistant",
95
+ # content: "2004"
96
+ # },
97
+ # ]
64
98
  #
65
99
  # @param prompt [String] The prompt to generate a chat completion for
66
- # @param messages [Array] The messages that have been sent in the conversation
67
- # @param context [String] The context of the conversation
68
- # @param examples [Array] Examples of messages provide model with
69
- # @param options extra parameters passed to OpenAI::Client#chat
70
- # @param block [Block] Pass the block to stream the response
100
+ # @param messages [Array<Hash>] The messages that have been sent in the conversation
101
+ # Each message should be a Hash with the following keys:
102
+ # - :content [String] The content of the message
103
+ # - :role [String] The role of the sender (system, user, assistant, or function)
104
+ # @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"
105
+ # @param examples [Array<Hash>] Examples of messages to provide to the model. Useful for Few-Shot Prompting
106
+ # Each message should be a Hash with the following keys:
107
+ # - :content [String] The content of the message
108
+ # - :role [String] The role of the sender (system, user, assistant, or function)
109
+ # @param options <Hash> extra parameters passed to OpenAI::Client#chat
110
+ # @yield [String] Stream responses back one String at a time
71
111
  # @return [String] The chat completion
72
112
  #
73
113
  def chat(prompt: "", messages: [], context: "", examples: [], **options)
74
114
  raise ArgumentError.new(":prompt or :messages argument is expected") if prompt.empty? && messages.empty?
75
115
 
76
- parameters = compose_parameters DEFAULTS[:chat_completion_model_name], options
116
+ parameters = compose_parameters @defaults[:chat_completion_model_name], options
77
117
  parameters[:messages] = compose_chat_messages(prompt: prompt, messages: messages, context: context, examples: examples)
78
118
  parameters[:max_tokens] = validate_max_tokens(parameters[:messages], parameters[:model])
79
119
 
@@ -104,13 +144,13 @@ module Langchain::LLM
104
144
  )
105
145
  prompt = prompt_template.format(text: text)
106
146
 
107
- complete(prompt: prompt, temperature: DEFAULTS[:temperature])
147
+ complete(prompt: prompt, temperature: @defaults[:temperature])
108
148
  end
109
149
 
110
150
  private
111
151
 
112
152
  def compose_parameters(model, params)
113
- default_params = {model: model, temperature: DEFAULTS[:temperature]}
153
+ default_params = {model: model, temperature: @defaults[:temperature]}
114
154
 
115
155
  default_params[:stop] = params.delete(:stop_sequences) if params[:stop_sequences]
116
156
 
@@ -32,7 +32,7 @@ module Langchain::LLM
32
32
  #
33
33
  # @param api_key [String] The API key to use
34
34
  #
35
- def initialize(api_key:)
35
+ def initialize(api_key:, default_options: {})
36
36
  depends_on "replicate-ruby"
37
37
  require "replicate"
38
38
 
@@ -41,6 +41,7 @@ module Langchain::LLM
41
41
  end
42
42
 
43
43
  @client = ::Replicate.client
44
+ @defaults = DEFAULTS.merge(default_options)
44
45
  end
45
46
 
46
47
  #
@@ -100,7 +101,7 @@ module Langchain::LLM
100
101
 
101
102
  complete(
102
103
  prompt: prompt,
103
- temperature: DEFAULTS[:temperature],
104
+ temperature: @defaults[:temperature],
104
105
  # Most models have a context length of 2048 tokens (except for the newest models, which support 4096).
105
106
  max_tokens: 2048
106
107
  )
@@ -111,11 +112,11 @@ module Langchain::LLM
111
112
  private
112
113
 
113
114
  def completion_model
114
- @completion_model ||= client.retrieve_model(DEFAULTS[:completion_model_name]).latest_version
115
+ @completion_model ||= client.retrieve_model(@defaults[:completion_model_name]).latest_version
115
116
  end
116
117
 
117
118
  def embeddings_model
118
- @embeddings_model ||= client.retrieve_model(DEFAULTS[:embeddings_model_name]).latest_version
119
+ @embeddings_model ||= client.retrieve_model(@defaults[:embeddings_model_name]).latest_version
119
120
  end
120
121
  end
121
122
  end
@@ -16,6 +16,11 @@ module Langchain::Tool
16
16
  Useful for getting the result of a math expression.
17
17
 
18
18
  The input to this tool should be a valid mathematical expression that could be executed by a simple calculator.
19
+ Usage:
20
+ Action Input: 1 + 1
21
+ Action Input: 3 * 2 / 4
22
+ Action Input: 9 - 7
23
+ Action Input: (4.1 + 2.3) / (2.0 - 5.6) * 3
19
24
  DESC
20
25
 
21
26
  def initialize
@@ -32,14 +37,7 @@ module Langchain::Tool
32
37
 
33
38
  Eqn::Calculator.calc(input)
34
39
  rescue Eqn::ParseError, Eqn::NoVariableValueError
35
- # Sometimes the input is not a pure math expression, e.g: "12F in Celsius"
36
- # We can use the google answer box to evaluate this expression
37
- # TODO: Figure out to find a better way to evaluate these language expressions.
38
- hash_results = Langchain::Tool::GoogleSearch
39
- .new(api_key: ENV["SERPAPI_API_KEY"])
40
- .execute_search(input: input)
41
- hash_results.dig(:answer_box, :to) ||
42
- hash_results.dig(:answer_box, :result)
40
+ "\"#{input}\" is an invalid mathematical expression"
43
41
  end
44
42
  end
45
43
  end
@@ -14,15 +14,18 @@ module Langchain::Tool
14
14
  The input to this tool should be valid SQL.
15
15
  DESC
16
16
 
17
- attr_reader :db
17
+ attr_reader :db, :requested_tables, :except_tables
18
18
 
19
19
  #
20
20
  # Establish a database connection
21
21
  #
22
22
  # @param connection_string [String] Database connection info, e.g. 'postgres://user:password@localhost:5432/db_name'
23
+ # @param tables [Array<Symbol>] The tables to use. Will use all if empty.
24
+ # @param except_tables [Array<Symbol>] The tables to exclude. Will exclude none if empty.
25
+
23
26
  # @return [Database] Database object
24
27
  #
25
- def initialize(connection_string:)
28
+ def initialize(connection_string:, tables: [], except_tables: [])
26
29
  depends_on "sequel"
27
30
  require "sequel"
28
31
  require "sequel/extensions/schema_dumper"
@@ -30,7 +33,8 @@ module Langchain::Tool
30
33
  raise StandardError, "connection_string parameter cannot be blank" if connection_string.empty?
31
34
 
32
35
  @db = Sequel.connect(connection_string)
33
- @db.extension :schema_dumper
36
+ @requested_tables = tables
37
+ @except_tables = except_tables
34
38
  end
35
39
 
36
40
  #
@@ -38,9 +42,26 @@ module Langchain::Tool
38
42
  #
39
43
  # @return [String] schema
40
44
  #
41
- def schema
42
- Langchain.logger.info("Dumping schema", for: self.class)
43
- db.dump_schema_migration(same_db: true, indexes: false) unless db.adapter_scheme == :mock
45
+ def dump_schema
46
+ Langchain.logger.info("Dumping schema tables and keys", for: self.class)
47
+ schema = ""
48
+ db.tables.each do |table|
49
+ next if except_tables.include?(table)
50
+ next unless requested_tables.empty? || requested_tables.include?(table)
51
+
52
+ schema << "CREATE TABLE #{table}(\n"
53
+ db.schema(table).each do |column|
54
+ schema << "#{column[0]} #{column[1][:type]}"
55
+ schema << " PRIMARY KEY" if column[1][:primary_key] == true
56
+ schema << "," unless column == db.schema(table).last
57
+ schema << "\n"
58
+ end
59
+ schema << ");\n"
60
+ db.foreign_key_list(table).each do |fk|
61
+ schema << "ALTER TABLE #{table} ADD FOREIGN KEY (#{fk[:columns][0]}) REFERENCES #{fk[:table]}(#{fk[:key][0]});\n"
62
+ end
63
+ end
64
+ schema
44
65
  end
45
66
 
46
67
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Langchain
4
- VERSION = "0.5.6"
4
+ VERSION = "0.5.7"
5
5
  end
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.5.6
4
+ version: 0.5.7
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-06-18 00:00:00.000000000 Z
11
+ date: 2023-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: baran