rach 0.2.8 → 0.2.10

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: aae006344d427b9f6b585ec28419beca527ee1659faeb6188e24b73a0c0d4eaf
4
- data.tar.gz: a7348260087b8829c8465d1a3a6a6129be0a7c467e2f93fdb4dcb753ee87ae24
3
+ metadata.gz: b9479cf41fe01b10e9c117939304120042337fa3a3ce8bc27fc1649e69894bcc
4
+ data.tar.gz: ea441f9a3978f58bc40284cfa5cefe49533f9a53275c8c85aca3761247dcf91b
5
5
  SHA512:
6
- metadata.gz: ac363b216035937755af0e712aaa2b40d871321a791f5bb2d8be7ca3f407a3fee7e36c5746cc982f3b361c52c9b044ea89624994809547155a0c5b62241a75d7
7
- data.tar.gz: 61238f0299ff7f149e49cfe2b4ab660aae899337e59783e0d54f91216e0a3cc990bec0aa0b1b81abf008e2c9c2468a463395250372d5f098b1bb29bfeb70882e
6
+ metadata.gz: 8e185fa5b250d9433a71af3e31089b046ed4087ace488e608e54184f444bc152f9e02f0a5515a2a878ff7d07c56537a53906b111c39c8ee9cd42edc39b2014b8
7
+ data.tar.gz: ee0e72f793aee837570acfddc55da33aee2b9a13161e813121de6abe6955877828ee7c20638a0731a40367a9c40ce1aacdda2edaa548f3c4fd0ac0a59b7ce31c
data/lib/rach/client.rb CHANGED
@@ -13,7 +13,7 @@ module Rach
13
13
  setup_providers(providers)
14
14
  elsif access_token && model
15
15
  provider = Provider.for(model)
16
- setup_providers({ provider.key => { access_token: access_token } })
16
+ setup_providers({ provider.key => { access_token: access_token, model: model } })
17
17
  else
18
18
  raise ArgumentError, "Either (providers) or (access_token AND model) must be provided"
19
19
  end
@@ -0,0 +1,105 @@
1
+ require 'gemini-ai'
2
+
3
+ module Rach
4
+ module Provider
5
+ class Google < Base
6
+
7
+ def initialize(access_token: nil, logger: nil, **kwargs)
8
+ @client = create_client(access_token, **kwargs)
9
+ @logger = logger
10
+ end
11
+
12
+ def chat(**parameters)
13
+ messages = parameters.dig(:parameters, :messages) || []
14
+ system_message = messages.find { |msg| msg[:role] == "system" }
15
+ messages = messages.reject { |msg| msg[:role] == "system" } if system_message
16
+
17
+ # Convert messages to Gemini format
18
+ contents = messages.map do |msg|
19
+ {
20
+ role: msg[:role] == "assistant" ? "model" : "user",
21
+ parts: { text: msg[:content] }
22
+ }
23
+ end
24
+
25
+ # If there's a system message, prepend it to user's first message
26
+ if system_message
27
+ first_user_message = contents.find { |msg| msg[:role] == "user" }
28
+ if first_user_message
29
+ first_user_message[:parts][:text] = "#{system_message[:content]}\n\n#{first_user_message[:parts][:text]}"
30
+ end
31
+ end
32
+
33
+ request_params = { contents: contents }
34
+
35
+ # Handle response format if provided
36
+ if response_format = parameters.dig(:parameters, :response_format)
37
+ request_params[:generation_config] = {
38
+ response_mime_type: 'application/json',
39
+ response_schema: convert_response_format(response_format)
40
+ }
41
+ end
42
+
43
+ if request_params.dig(:generation_config, :response_schema)
44
+ request_params[:generation_config][:response_schema].delete(:additionalProperties)
45
+ request_params[:generation_config][:response_schema].delete(:required)
46
+ end
47
+
48
+ if @logger
49
+ @logger.info("Making API call to Google Gemini")
50
+ @logger.info("Request parameters: #{request_params.inspect}")
51
+ end
52
+
53
+ raw_response = @client.generate_content(request_params)
54
+
55
+ if @logger
56
+ @logger.info("Response: #{raw_response.inspect}")
57
+ end
58
+
59
+ Response.new(
60
+ id: raw_response["candidates"][0]["content"]["parts"][0]["text"],
61
+ model: raw_response["modelVersion"],
62
+ content: raw_response["candidates"][0]["content"]["parts"][0]["text"],
63
+ usage: {
64
+ "prompt_tokens" => raw_response["usageMetadata"]["promptTokenCount"],
65
+ "completion_tokens" => raw_response["usageMetadata"]["candidatesTokenCount"],
66
+ "total_tokens" => raw_response["usageMetadata"]["totalTokenCount"]
67
+ },
68
+ raw_response: raw_response
69
+ )
70
+ end
71
+
72
+ def self.supports?(model)
73
+ model.start_with?("gemini")
74
+ end
75
+
76
+ private
77
+
78
+ def create_client(access_token, **config)
79
+ client_config = {
80
+ credentials: {
81
+ service: 'generative-language-api',
82
+ api_key: access_token
83
+ },
84
+ options: { model: config[:model] }
85
+ }
86
+
87
+ # Only merge additional options that aren't already set
88
+ client_config[:options].merge!(config.except(:model))
89
+
90
+ # Function calling and structured output are broken in this gem
91
+ gemini = ::Gemini.new(**client_config)
92
+ base_address = gemini.instance_variable_get(:@base_address)
93
+ base_address.gsub!('v1', 'v1beta') if base_address
94
+ gemini
95
+ end
96
+
97
+ def convert_response_format(format)
98
+ return unless format[:type] == "json_schema"
99
+
100
+ schema = format.dig(:json_schema, :schema)
101
+ schema.deep_symbolize_keys
102
+ end
103
+ end
104
+ end
105
+ end
data/lib/rach/provider.rb CHANGED
@@ -3,7 +3,8 @@ module Rach
3
3
 
4
4
  AVAILABLE_PROVIDERS = [
5
5
  Provider::OpenAI,
6
- Provider::Anthropic
6
+ Provider::Anthropic,
7
+ Provider::Google
7
8
  ].to_h { |p| [p.key, p] }.freeze
8
9
 
9
10
  def self.for(model)
data/lib/rach/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Rach
2
- VERSION = "0.2.8"
2
+ VERSION = "0.2.10"
3
3
  end
4
4
 
data/lib/rach.rb CHANGED
@@ -14,6 +14,7 @@ require_relative "rach/function"
14
14
  require_relative "rach/provider/base"
15
15
  require_relative "rach/provider/openai"
16
16
  require_relative "rach/provider/anthropic"
17
+ require_relative "rach/provider/google"
17
18
  require_relative "rach/provider"
18
19
  require_relative "rach/prompt"
19
20
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rach
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.2.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roger Garcia
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-01-30 00:00:00.000000000 Z
11
+ date: 2025-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -100,6 +100,20 @@ dependencies:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
102
  version: '7.3'
103
+ - !ruby/object:Gem::Dependency
104
+ name: gemini-ai
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: 4.2.0
110
+ type: :runtime
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: 4.2.0
103
117
  description: Rach is a lightweight framework for orchestrating AI agents
104
118
  email:
105
119
  - rach@rogergarcia.me
@@ -119,6 +133,7 @@ files:
119
133
  - lib/rach/provider.rb
120
134
  - lib/rach/provider/anthropic.rb
121
135
  - lib/rach/provider/base.rb
136
+ - lib/rach/provider/google.rb
122
137
  - lib/rach/provider/openai.rb
123
138
  - lib/rach/response.rb
124
139
  - lib/rach/response_format.rb