fix(captain): scope model routing to app provider

This commit is contained in:
aakashb95
2026-06-26 14:11:26 +05:30
parent 7793e4645f
commit 4fd1a5c2ef
17 changed files with 203 additions and 69 deletions
+20 -25
View File
@@ -1,4 +1,8 @@
require_relative 'provider_model_catalog'
module Llm::Models
extend Llm::ProviderModelCatalog
CONFIG = YAML.load_file(Rails.root.join('config/llm.yml')).freeze
OPENAI_ONLY_FEATURES = %w[audio_transcription help_center_search].freeze
@@ -16,18 +20,23 @@ module Llm::Models
end
def default_model_for(feature)
default_model = features.dig(feature.to_s, 'default')
return default_model if supported_model?(default_model)
installation_default = installation_model
return installation_default if valid_model_for?(feature, installation_default)
feature_default = features.dig(feature.to_s, 'default')
return feature_default if valid_model_for?(feature, feature_default)
models_for(feature).first
end
def models_for(feature)
(configured_models_for(feature) + provider_models_for(feature)).uniq
def models_for(feature, provider: provider_for_feature(feature))
(configured_models_for(feature, provider: provider) + provider_models_for(feature, provider: provider)).uniq
end
def valid_model_for?(feature, model_name)
models_for(feature).include?(model_name.to_s)
def valid_model_for?(feature, model_name, provider: provider_for_feature(feature))
return false if model_name.blank?
models_for(feature, provider: provider).include?(model_name.to_s)
end
def model_config(model_name)
@@ -53,8 +62,10 @@ module Llm::Models
feature = features[feature_key.to_s]
return nil unless feature
provider = provider_for_feature(feature_key)
{
models: models_for(feature_key).map do |model_name|
models: models_for(feature_key, provider: provider).map do |model_name|
model = model_config(model_name)
{
id: model_name,
@@ -64,29 +75,13 @@ module Llm::Models
credit_multiplier: model['credit_multiplier']
}
end,
default: feature['default']
default: default_model_for(feature_key),
provider: provider
}
end
private
def configured_models_for(feature)
(features.dig(feature.to_s, 'models') || []).select { |model_name| supported_model?(model_name) }
end
def provider_models_for(feature)
return [] if openai_only_feature?(feature)
provider = Llm::Config.current_provider
return [] if provider == Llm::Config::DEFAULT_PROVIDER
RubyLLM.models.by_provider(provider).chat_models.map(&:id)
end
def openai_only_feature?(feature)
OPENAI_ONLY_FEATURES.include?(feature.to_s)
end
def ruby_llm_model_config(model_name)
model = ruby_llm_model(model_name)
return unless model
+11 -6
View File
@@ -2,6 +2,9 @@ require 'ruby_llm'
module Llm::ProviderConfig
PROVIDER_CONFIG_PREFIX = 'CAPTAIN_LLM'.freeze
PROVIDER_CONFIG_KEY = 'CAPTAIN_LLM_PROVIDER'.freeze
MODEL_CONFIG_KEY = 'CAPTAIN_LLM_MODEL'.freeze
LEGACY_OPENAI_MODEL_CONFIG_KEY = 'CAPTAIN_OPEN_AI_MODEL'.freeze
LEGACY_CONFIG_KEYS = {
openai_api_key: 'CAPTAIN_OPEN_AI_API_KEY',
@@ -22,20 +25,22 @@ module Llm::ProviderConfig
end
end
def provider_config_keys
(['CAPTAIN_LLM_PROVIDER'] + provider_config_options.values).uniq
def provider_config_keys(provider = nil)
([PROVIDER_CONFIG_KEY, MODEL_CONFIG_KEY] + provider_config_options(provider).values).uniq
end
def provider_config_options
provider_options.keys.each_with_object({}) do |provider, result|
provider_configuration_options(provider).each do |option|
def provider_config_options(provider = nil)
providers = provider.present? ? [provider.to_s] : provider_options.keys
providers.each_with_object({}) do |provider_name, result|
provider_configuration_options(provider_name).each do |option|
result[option] = installation_config_name(option)
end
end
end
def current_provider
provider = InstallationConfig.find_by(name: 'CAPTAIN_LLM_PROVIDER')&.value.presence
provider = InstallationConfig.find_by(name: PROVIDER_CONFIG_KEY)&.value.presence
return provider if provider_options.key?(provider)
Llm::Config::DEFAULT_PROVIDER
+63
View File
@@ -0,0 +1,63 @@
module Llm::ProviderModelCatalog
def installation_model(provider: provider_for_feature(nil))
model = InstallationConfig.find_by(name: Llm::ProviderConfig::MODEL_CONFIG_KEY)&.value.presence
return model if model_provider?(model, provider)
legacy_model = InstallationConfig.find_by(name: Llm::ProviderConfig::LEGACY_OPENAI_MODEL_CONFIG_KEY)&.value.presence
return legacy_model if provider == Llm::Config::DEFAULT_PROVIDER && model_provider?(legacy_model, provider)
end
def provider_default_model_options(provider = Llm::Config.current_provider)
models_for_provider(provider).index_with do |model_name|
model = model_config(model_name)
model['display_name'] || model_name
end
end
private
def provider_for_feature(feature)
return Llm::Config::DEFAULT_PROVIDER if openai_only_feature?(feature)
Llm::Config.current_provider
end
def configured_models_for(feature, provider:)
(features.dig(feature.to_s, 'models') || []).select do |model_name|
supported_model?(model_name) && model_provider?(model_name, provider)
end
end
def provider_models_for(feature, provider:)
return [] if openai_only_feature?(feature)
return [] if provider == Llm::Config::DEFAULT_PROVIDER
models_for_provider(provider)
end
def models_for_provider(provider)
configured_provider_models = models.filter_map do |model_name, config|
model_name if config['provider'] == provider.to_s && chat_model?(model_name)
end
(configured_provider_models + ruby_llm_provider_models(provider)).uniq
end
def ruby_llm_provider_models(provider)
return [] if provider.to_s == Llm::Config::DEFAULT_PROVIDER
RubyLLM.models.by_provider(provider.to_s).chat_models.map(&:id)
end
def chat_model?(model_name)
Llm::Models::OPENAI_ONLY_FEATURES.none? { |feature| features.dig(feature, 'models')&.include?(model_name) }
end
def model_provider?(model_name, provider)
provider_for(model_name) == provider.to_s
end
def openai_only_feature?(feature)
Llm::Models::OPENAI_ONLY_FEATURES.include?(feature.to_s)
end
end