-
Notifications
You must be signed in to change notification settings - Fork 102
feat: add Google Gemini as alternative LLM provider #1968
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,8 @@ class ValidationError < StandardError; end | |
| field :openai_uri_base, type: :string, default: ENV["OPENAI_URI_BASE"] | ||
| field :openai_model, type: :string, default: ENV["OPENAI_MODEL"] | ||
| field :openai_json_mode, type: :string, default: ENV["LLM_JSON_MODE"] | ||
| field :gemini_api_key, type: :string, default: ENV["GEMINI_API_KEY"] | ||
| field :gemini_model, type: :string, default: ENV["GEMINI_MODEL"] | ||
|
Comment on lines
+13
to
+14
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win Mirror cache invalidation behavior for
💡 Suggested parity fix class << self
alias_method :raw_onboarding_state, :onboarding_state
alias_method :raw_onboarding_state=, :onboarding_state=
alias_method :raw_openai_model, :openai_model
alias_method :raw_openai_model=, :openai_model=
+ alias_method :raw_gemini_model, :gemini_model
+ alias_method :raw_gemini_model=, :gemini_model=
@@
def openai_model=(value)
old_value = raw_openai_model
self.raw_openai_model = value
if old_value != value && old_value.present?
Rails.logger.info("OpenAI model changed from #{old_value} to #{value}, clearing AI cache for all families")
Family.find_each do |family|
ClearAiCacheJob.perform_later(family)
end
end
end
+
+ def gemini_model=(value)
+ old_value = raw_gemini_model
+ self.raw_gemini_model = value
+
+ if old_value != value && old_value.present?
+ Rails.logger.info("Gemini model changed from #{old_value} to #{value}, clearing AI cache for all families")
+ Family.find_each do |family|
+ ClearAiCacheJob.perform_later(family)
+ end
+ end
+ end
end🤖 Prompt for AI Agents |
||
|
|
||
| # LLM token budget (applies to every outbound LLM call: chat, auto-categorize, | ||
| # merchant detection, enhance-merchants, PDF processing). Defaults track | ||
|
|
@@ -70,6 +72,7 @@ module EncryptedSettingFields | |
| eodhd_api_key | ||
| alpha_vantage_api_key | ||
| openai_access_token | ||
| gemini_api_key | ||
| external_assistant_token | ||
| ].freeze | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| <div class="space-y-4"> | ||
| <div> | ||
| <h2 class="font-medium mb-1"><%= t(".title") %></h2> | ||
| <% if ENV["GEMINI_API_KEY"].present? %> | ||
| <p class="text-sm text-secondary"><%= t(".env_configured_message") %></p> | ||
| <% else %> | ||
| <p class="text-secondary text-sm mb-4"><%= t(".description") %></p> | ||
| <% end %> | ||
| </div> | ||
|
|
||
| <%= styled_form_with model: Setting.new, | ||
| url: settings_hosting_path, | ||
| method: :patch, | ||
| class: "space-y-4", | ||
| data: { | ||
| controller: "auto-submit-form", | ||
| "auto-submit-form-trigger-event-value": "blur" | ||
| } do |form| %> | ||
| <%= form.password_field :gemini_api_key, | ||
| label: t(".api_key_label"), | ||
| placeholder: t(".api_key_placeholder"), | ||
| value: (Setting.gemini_api_key.present? ? "********" : nil), | ||
| autocomplete: "off", | ||
| autocapitalize: "none", | ||
| spellcheck: "false", | ||
| inputmode: "text", | ||
| disabled: ENV["GEMINI_API_KEY"].present?, | ||
| data: { "auto-submit-form-target": "auto" } %> | ||
| <p class="text-xs text-secondary mt-1"><%= t(".api_key_help_html") %></p> | ||
|
|
||
| <%= form.text_field :gemini_model, | ||
| label: t(".model_label"), | ||
| placeholder: t(".model_placeholder"), | ||
| value: Setting.gemini_model, | ||
| autocomplete: "off", | ||
| autocapitalize: "none", | ||
| spellcheck: "false", | ||
| inputmode: "text", | ||
| disabled: ENV["GEMINI_MODEL"].present?, | ||
| data: { "auto-submit-form-target": "auto" } %> | ||
| <p class="text-xs text-secondary mt-1"><%= t(".model_help_html") %></p> | ||
| <% end %> | ||
| </div> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| <div class="space-y-4"> | ||
| <p class="text-secondary text-sm"><%= t(".description") %></p> | ||
|
|
||
| <%= styled_form_with model: Setting.new, | ||
| url: settings_hosting_path, | ||
| method: :patch, | ||
| class: "space-y-4", | ||
| data: { | ||
| controller: "auto-submit-form", | ||
| "auto-submit-form-trigger-event-value": "blur" | ||
| } do |form| %> | ||
| <%= form.number_field :llm_context_window, | ||
| label: t(".context_window_label"), | ||
| placeholder: "2048", | ||
| value: Setting.llm_context_window, | ||
| min: 256, | ||
| disabled: ENV["LLM_CONTEXT_WINDOW"].present?, | ||
| data: { "auto-submit-form-target": "auto" } %> | ||
| <p class="text-xs text-secondary mt-1 mb-3"><%= t(".context_window_help") %></p> | ||
|
|
||
| <%= form.number_field :llm_max_response_tokens, | ||
| label: t(".max_response_tokens_label"), | ||
| placeholder: "512", | ||
| value: Setting.llm_max_response_tokens, | ||
| min: 64, | ||
| disabled: ENV["LLM_MAX_RESPONSE_TOKENS"].present?, | ||
| data: { "auto-submit-form-target": "auto" } %> | ||
| <p class="text-xs text-secondary mt-1 mb-3"><%= t(".max_response_tokens_help") %></p> | ||
|
|
||
| <%= form.number_field :llm_max_items_per_call, | ||
| label: t(".max_items_per_call_label"), | ||
| placeholder: "25", | ||
| value: Setting.llm_max_items_per_call, | ||
| min: 1, | ||
| disabled: ENV["LLM_MAX_ITEMS_PER_CALL"].present?, | ||
| data: { "auto-submit-form-target": "auto" } %> | ||
| <p class="text-xs text-secondary mt-1"><%= t(".max_items_per_call_help") %></p> | ||
| <% end %> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returning
geminifromopenaihere changes allProvider::Registry.get_provider(:openai)call sites to use Gemini when no OpenAI key is set, but some of those paths still pass OpenAI model names explicitly. For example,Assistant::Function::ImportBankStatement#executealways sendsmodel: openai_model(defaulting togpt-4.1), andProvider::Openai#extract_bank_statementdoes not override that model for custom providers, so Gemini-only deployments will send an invalid model and fail extraction requests.Useful? React with 👍 / 👎.