gen-ai 0.4.0.alpha.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c449d6e75aa475881a045c2a03cf1b9fb0847c4d2c532478fbfa71f8829abaca
4
- data.tar.gz: 3aae7fcb6cb92ebe5bd160ed541815e715cabdaecbe3def48b65c92ada744aaf
3
+ metadata.gz: 43e7d6407d84beec138ffb2a176910fd1cbe25b0f67cc1cf04f7cd462a615617
4
+ data.tar.gz: 3ed1891455618eb80e9410a455d954ab547092d3f3aaa3c2302857917456f9b8
5
5
  SHA512:
6
- metadata.gz: a0c3b72951345e13c6840aa2982b325d54bea8c8ee4fdca7a6b8759fd7453f0d92c5405f3598da4542431b615293dec76099d08baee59239b8304d162cca5ec0
7
- data.tar.gz: 10b9d0e1328e2d19adbad9cb92c1f86aa545634195bcb33db0e1a52ed1b877723b86c5acd8d13f8ff6eaa022aef2e46fb049b204cd21db654ce4de482061eca5
6
+ metadata.gz: d690ce67be4296396f0f07e0c0c8873c19c3477191f23a2043b95ef4d771f57da159e96c26c599da09c5b5ca5d65769b2156c7a625771b5ac7dab310c9ced46b
7
+ data.tar.gz: 355662f601a9bf54f45afa1e026004cee58561d5581bb621f74da11a479081566e2260f1db4a969a126f68e6b825a7ad6ee339233ec5b2bd81438110cb290b4e
data/README.md CHANGED
@@ -32,10 +32,12 @@ require 'gen_ai'
32
32
 
33
33
  Language models capabilities
34
34
 
35
- | Provider | Embedding | Completion | Conversation | Sentiment | Summarization |
36
- | ---------------- | :-------: | :--------: | :----------: | :-------: | :-----------: |
37
- | **OpenAI** | ✅ | ✅ | ✅ | 🛠️ | 🛠️ |
38
- | **Google Palm2** | ✅ | ✅ | ✅ | 🛠️ | 🛠️ |
35
+ | Provider | Embedding | Completion | Conversation | Sentiment | Summarization |
36
+ | ----------------- | :-------: | :--------: | :----------: | :-------: | :-----------: |
37
+ | **OpenAI** | ✅ | ✅ | ✅ | 🛠️ | 🛠️ |
38
+ | **Google Palm2** | ✅ | ✅ | ✅ | 🛠️ | 🛠️ |
39
+ | **Google Gemini** | ❌ | 🛠️ | ✅ | 🛠️ | 🛠️ |
40
+ | **Anthropic** | ❌ | ✅ | ✅ | 🛠️ | 🛠️ |
39
41
 
40
42
  Image generation model capabilities
41
43
 
@@ -86,21 +88,34 @@ result.values
86
88
 
87
89
  ```
88
90
 
89
- Have a **conversation** with Large Language Model.
91
+ ### Chat
92
+ Have a **conversation** with Large Language Model and Build your own AI chatbot.
93
+
94
+ Setting a context for the conversation is optional, but it helps the model to understand the topic of the conversation.
90
95
 
91
96
  ```ruby
92
- result = model.chat('Hi, how are you?')
97
+ chat = GenAI::Chat.new(:open_ai, ENV['OPEN_AI_TOKEN'])
98
+ chat.start(context: "You are a chat bot named Erl")
99
+ chat.message("Hi, what's your name")
93
100
  # = >#<GenAI::Result:0x0000000106ff3d20...>
94
101
 
95
102
  result.value
96
- # => "Hello! I'm an AI, so I don't have feelings, but I'm here to help. How can I assist you today?"
103
+ # => "I am a chatbot and you can call me Erl. How can I help you?""
104
+
105
+ ```
97
106
 
107
+
108
+ Provider a history of the conversation to the model to help it understand the context of the conversation.
109
+
110
+ ```ruby
98
111
  history = [
99
112
  {role: 'user', content: 'What is the capital of Great Britain?'},
100
113
  {role: 'assistant', content: 'London'},
101
114
  ]
102
115
 
103
- result = model.chat("what about France?", history: history)
116
+ chat = GenAI::Chat.new(:open_ai, ENV['OPEN_AI_TOKEN'])
117
+ result = model.start(history: history)
118
+ result = model.message("what about France?")
104
119
  # => #<GenAI::Result:0x00000001033c3bc0...>
105
120
 
106
121
  result.value
@@ -6,9 +6,10 @@ require 'faraday/multipart'
6
6
  module GenAI
7
7
  module Api
8
8
  class Client
9
- def initialize(url:, token:)
9
+ def initialize(url:, token:, headers: {})
10
10
  @url = url
11
11
  @token = token
12
+ @headers = headers
12
13
  end
13
14
 
14
15
  def post(path, body, options = {})
@@ -28,19 +29,24 @@ module GenAI
28
29
 
29
30
  private
30
31
 
31
- attr_reader :url, :token
32
+ attr_reader :url, :token, :headers
32
33
 
33
34
  def connection(multipart: false)
34
- Faraday.new(url: url, headers: {
35
- 'Accept' => 'application/json',
36
- 'Content-Type' => multipart ? 'multipart/form-data' : 'application/json',
37
- 'Authorization' => "Bearer #{token}"
38
- }) do |conn|
35
+ Faraday.new(url: url, headers: build_headers(token, headers, multipart)) do |conn|
39
36
  conn.request :multipart if multipart
40
37
  conn.request :url_encoded
41
38
  end
42
39
  end
43
40
 
41
+ def build_headers(token, headers, multipart)
42
+ hash = {
43
+ 'Accept' => 'application/json',
44
+ 'Content-Type' => multipart ? 'multipart/form-data' : 'application/json'
45
+ }
46
+ hash['Authorization'] = "Bearer #{token}" if token
47
+ hash.merge(headers)
48
+ end
49
+
44
50
  def handle_response
45
51
  response = yield
46
52
 
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GenAI
4
+ module Api
5
+ module Format
6
+ module Anthropic
7
+ def format_messages(messages)
8
+ messages.map(&:deep_symbolize_keys)
9
+ end
10
+
11
+ def extract_completions(response)
12
+ if response['type'] == 'completion'
13
+ [response['completion'].strip]
14
+ else
15
+ response['content'].map { |item| item['text'] }
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -4,12 +4,19 @@ module GenAI
4
4
  module Api
5
5
  module Format
6
6
  module Gemini
7
+ USER_ROLE = 'user'
8
+ ASSISTANT_ROLE = 'model'
9
+
7
10
  def format_messages(messages)
8
- messages.map { |message| transform_message(message) }
11
+ messages.map { |message| transform_message(message.deep_symbolize_keys) }
9
12
  end
10
13
 
11
14
  def transform_message(message)
12
- { role: role_for(message), parts: [text: message[:content]] }
15
+ if message.keys == %i[role content]
16
+ { role: role_for(message), parts: [text: message[:content]] }
17
+ else
18
+ message
19
+ end
13
20
  end
14
21
 
15
22
  def extract_completions(response)
@@ -19,7 +26,7 @@ module GenAI
19
26
  private
20
27
 
21
28
  def role_for(message)
22
- message[:role] == 'user' ? self.class::USER_ROLE : self.class::ASSISTANT_ROLE
29
+ message[:role] == 'user' ? USER_ROLE : ASSISTANT_ROLE
23
30
  end
24
31
  end
25
32
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GenAI
4
+ class Chat
5
+ class Anthropic < Base
6
+ SYSTEM_ROLE = 'system'
7
+
8
+ private
9
+
10
+ def build_history(messages, context, examples)
11
+ @default_options[:system] = context
12
+ history = []
13
+ history.concat(examples)
14
+ history.concat(messages)
15
+ history
16
+ end
17
+
18
+ def role(message)
19
+ message[:role]
20
+ end
21
+
22
+ def transform_message(message)
23
+ message
24
+ end
25
+
26
+ def append_to_message(message)
27
+ @history.last[:content] = "#{@history.last[:content]}\n#{message}"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -6,23 +6,26 @@ module GenAI
6
6
  USER_ROLE = 'user'
7
7
  ASSISTANT_ROLE = 'assistant'
8
8
 
9
+ attr_reader :history, :default_options
10
+
9
11
  def initialize(provider:, token:, options: {})
10
12
  @history = []
13
+ @default_options = {}
11
14
  @model = GenAI::Language.new(provider, token, options: options)
12
15
  end
13
16
 
14
17
  def start(history: [], context: nil, examples: [])
15
- @history = build_history(history.map(&:deep_symbolize_keys!), context, examples.map(&:deep_symbolize_keys!))
18
+ @history = build_history(history.map(&:deep_symbolize_keys), context, examples.map(&:deep_symbolize_keys))
16
19
  end
17
20
 
18
21
  def message(message, options = {})
19
- if @history.size == 1
22
+ if @history.size == 1 && @history.first[:role] != 'system'
20
23
  append_to_message(message)
21
24
  else
22
25
  append_to_history({ role: USER_ROLE, content: message })
23
26
  end
24
27
 
25
- response = @model.chat(@history.dup, options)
28
+ response = @model.chat(@history.dup, default_options.merge(options).compact)
26
29
  append_to_history({ role: ASSISTANT_ROLE, content: response.value })
27
30
  response
28
31
  end
@@ -5,9 +5,6 @@ module GenAI
5
5
  class Gemini < Base
6
6
  include GenAI::Api::Format::Gemini
7
7
 
8
- USER_ROLE = 'user'
9
- ASSISTANT_ROLE = 'model'
10
-
11
8
  private
12
9
 
13
10
  def build_history(messages, context, examples)
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+
5
+ module GenAI
6
+ class Language
7
+ class Anthropic < Base
8
+ include GenAI::Api::Format::Anthropic
9
+
10
+ BASE_API_URL = 'https://api.anthropic.com'
11
+ ANTHROPIC_VERSION = '2023-06-01'
12
+ ANTHROPIC_BETA = 'messages-2023-12-15'
13
+ COMPLETION_MODEL = 'claude-2.1'
14
+ DEFAULT_MAX_TOKENS = 1024
15
+
16
+ def initialize(token:, options: {})
17
+ @token = token
18
+ build_client(token)
19
+ end
20
+
21
+ def complete(prompt, options = {})
22
+ response = client.post '/v1/complete', {
23
+ prompt: "\n\nHuman: #{prompt}\n\nAssistant:",
24
+ model: options.delete(:model) || COMPLETION_MODEL,
25
+ max_tokens_to_sample: options.delete(:max_tokens_to_sample) || DEFAULT_MAX_TOKENS
26
+ }.merge(options)
27
+
28
+ build_result(model: COMPLETION_MODEL, raw: response, parsed: extract_completions(response))
29
+ end
30
+
31
+ def chat(messages, options = {})
32
+ response = client.post '/v1/messages', {
33
+ messages: format_messages(messages),
34
+ model: options.delete(:model) || COMPLETION_MODEL,
35
+ max_tokens: options.delete(:max_tokens) || DEFAULT_MAX_TOKENS
36
+ }.merge(options)
37
+
38
+ build_result(model: COMPLETION_MODEL, raw: response, parsed: extract_completions(response))
39
+ end
40
+
41
+ private
42
+
43
+ def build_client(token)
44
+ @client = GenAI::Api::Client.new(url: BASE_API_URL, token: nil, headers: {
45
+ 'anthropic-beta' => ANTHROPIC_BETA,
46
+ 'anthropic-version' => ANTHROPIC_VERSION,
47
+ 'x-api-key' => token
48
+ })
49
+ end
50
+ end
51
+ end
52
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'faraday'
4
4
 
5
+ require 'pry'
5
6
  module GenAI
6
7
  class Language
7
8
  class Gemini < Base
@@ -18,7 +19,7 @@ module GenAI
18
19
 
19
20
  def chat(messages, options = {})
20
21
  response = client.post "/v1beta/models/gemini-pro:generateContent?key=#{@token}", {
21
- contents: messages.map(&:deep_symbolize_keys!),
22
+ contents: format_messages(messages),
22
23
  generationConfig: options.except(:model)
23
24
  }
24
25
 
@@ -32,7 +32,7 @@ module GenAI
32
32
 
33
33
  def chat(messages, options = {})
34
34
  parameters = {
35
- messages: messages.map(&:deep_symbolize_keys!),
35
+ messages: messages.map(&:deep_symbolize_keys),
36
36
  model: options.delete(:model) || COMPLETION_MODEL
37
37
  }.merge(options)
38
38
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GenAI
4
- VERSION = '0.4.0.alpha.3'
4
+ VERSION = '0.4.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gen-ai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0.alpha.3
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Chaplinsky
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-03 00:00:00.000000000 Z
11
+ date: 2024-01-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -113,10 +113,12 @@ files:
113
113
  - lib/gen/ai.rb
114
114
  - lib/gen_ai.rb
115
115
  - lib/gen_ai/api/client.rb
116
+ - lib/gen_ai/api/format/anthropic.rb
116
117
  - lib/gen_ai/api/format/gemini.rb
117
118
  - lib/gen_ai/api/format/open_ai.rb
118
119
  - lib/gen_ai/base.rb
119
120
  - lib/gen_ai/chat.rb
121
+ - lib/gen_ai/chat/anthropic.rb
120
122
  - lib/gen_ai/chat/base.rb
121
123
  - lib/gen_ai/chat/gemini.rb
122
124
  - lib/gen_ai/chat/google_palm.rb
@@ -127,6 +129,7 @@ files:
127
129
  - lib/gen_ai/image/open_ai.rb
128
130
  - lib/gen_ai/image/stability_ai.rb
129
131
  - lib/gen_ai/language.rb
132
+ - lib/gen_ai/language/anthropic.rb
130
133
  - lib/gen_ai/language/base.rb
131
134
  - lib/gen_ai/language/gemini.rb
132
135
  - lib/gen_ai/language/google_palm.rb
@@ -153,9 +156,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
153
156
  version: 2.7.0
154
157
  required_rubygems_version: !ruby/object:Gem::Requirement
155
158
  requirements:
156
- - - ">"
159
+ - - ">="
157
160
  - !ruby/object:Gem::Version
158
- version: 1.3.1
161
+ version: '0'
159
162
  requirements: []
160
163
  rubygems_version: 3.3.7
161
164
  signing_key: