Class: PromptObjects::LLM::Factory

Inherits:
Object
  • Object
show all
Defined in:
lib/prompt_objects/llm/factory.rb

Overview

Factory for creating LLM adapters based on provider name. Provides a unified interface for switching between providers.

Constant Summary collapse

PROVIDERS =
{
  "openai" => {
    adapter: "OpenAIAdapter",
    env_key: "OPENAI_API_KEY",
    default_model: "gpt-5.6-luna",
    models: %w[gpt-5.6-luna gpt-5.6-terra gpt-5.6-sol gpt-5.4-mini gpt-5.4-nano gpt-5.2 gpt-4.1 gpt-4.1-mini]
  },
  "anthropic" => {
    adapter: "AnthropicAdapter",
    env_key: "ANTHROPIC_API_KEY",
    default_model: "claude-haiku-4-5",
    models: %w[claude-haiku-4-5 claude-sonnet-4-5 claude-opus-4]
  },
  "gemini" => {
    adapter: "GeminiAdapter",
    env_key: "GEMINI_API_KEY",
    default_model: "gemini-3.5-flash",
    models: %w[gemini-3.5-flash gemini-3.1-flash-lite gemini-3-flash-preview gemini-2.5-pro gemini-2.5-flash]
  },
  "ollama" => {
    adapter: "OpenAIAdapter",
    env_key: nil,
    api_key_default: "ollama",
    default_model: "llama3.2",
    models: [],  # Dynamic — populated from Ollama API
    base_url: "http://localhost:11434/v1",
    local: true
  },
  "openrouter" => {
    adapter: "OpenAIAdapter",
    env_key: "OPENROUTER_API_KEY",
    default_model: "meta-llama/llama-3.3-70b-instruct",
    models: %w[
      meta-llama/llama-3.3-70b-instruct
      meta-llama/llama-4-scout
      meta-llama/llama-4-maverick
      mistralai/mistral-large-2411
      google/gemma-3-27b-it
      deepseek/deepseek-r1
      qwen/qwen-2.5-72b-instruct
    ],
    base_url: "https://openrouter.ai/api/v1",
    extra_headers: {
      "HTTP-Referer" => "https://github.com/prompt-objects",
      "X-Title" => "PromptObjects"
    }
  }
}.freeze
DEFAULT_PROVIDER =
"anthropic"

Class Method Summary collapse

Class Method Details

.available_providersHash<String, Boolean>

Check which providers are available (have API keys or are local).

Returns:

  • (Hash<String, Boolean>)


105
106
107
108
109
110
111
112
113
114
115
# File 'lib/prompt_objects/llm/factory.rb', line 105

def available_providers
  PROVIDERS.transform_values do |config|
    if config[:local]
      check_local_provider(config[:base_url])
    elsif config[:env_key]
      ENV.key?(config[:env_key])
    else
      true
    end
  end
end

.create(provider: nil, model: nil, api_key: nil) ⇒ OpenAIAdapter, ...

Create an adapter for the given provider.

Parameters:

  • provider (String) (defaults to: nil)

    Provider name

  • model (String, nil) (defaults to: nil)

    Optional model override

  • api_key (String, nil) (defaults to: nil)

    Optional API key override

Returns:

Raises:



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/prompt_objects/llm/factory.rb', line 68

def create(provider: nil, model: nil, api_key: nil)
  provider_name = (provider || DEFAULT_PROVIDER).to_s.downcase
  config = PROVIDERS[provider_name]

  raise Error, "Unknown LLM provider: #{provider_name}" unless config

  # Resolve API key: explicit > env var > default
  resolved_key = api_key ||
    (config[:env_key] && ENV[config[:env_key]]) ||
    config[:api_key_default]

  adapter_class = LLM.const_get(config[:adapter])

  adapter_args = { api_key: resolved_key, model: model || config[:default_model] }
  adapter_args[:base_url] = config[:base_url] if config[:base_url]
  adapter_args[:extra_headers] = config[:extra_headers] if config[:extra_headers]
  # Pass provider name for usage tracking (OpenAI adapter handles multiple providers)
  adapter_args[:provider_name] = provider_name if config[:adapter] == "OpenAIAdapter"

  adapter_class.new(**adapter_args)
end

.default_model(provider) ⇒ String?

Get the default model for a provider.

Parameters:

  • provider (String)

    Provider name

Returns:

  • (String, nil)


120
121
122
# File 'lib/prompt_objects/llm/factory.rb', line 120

def default_model(provider)
  PROVIDERS.dig(provider.to_s.downcase, :default_model)
end

.discover_ollama_models(base_url = "http://localhost:11434/v1") ⇒ Array<String>

Discover installed Ollama models.

Parameters:

  • base_url (String) (defaults to: "http://localhost:11434/v1")

    Ollama API base URL (with /v1 suffix)

Returns:

  • (Array<String>)

    Model names



144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/prompt_objects/llm/factory.rb', line 144

def discover_ollama_models(base_url = "http://localhost:11434/v1")
  # Ollama's model list endpoint is at /api/tags (not under /v1)
  tags_url = base_url.sub(%r{/v1\z}, "") + "/api/tags"
  uri = URI(tags_url)
  response = Net::HTTP.get_response(uri)
  return [] unless response.is_a?(Net::HTTPSuccess)

  data = JSON.parse(response.body)
  (data["models"] || []).map { |m| m["name"] }
rescue StandardError
  []
end

.models_for(provider) ⇒ Array<String>

Get available models for a provider. For Ollama, dynamically discovers installed models.

Parameters:

  • provider (String)

    Provider name

Returns:

  • (Array<String>)


128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/prompt_objects/llm/factory.rb', line 128

def models_for(provider)
  config = PROVIDERS[provider.to_s.downcase]
  return [] unless config

  # Dynamic model discovery for Ollama
  if config[:local] && provider.to_s.downcase == "ollama"
    models = discover_ollama_models(config[:base_url])
    return models unless models.empty?
  end

  config[:models] || []
end

.provider_info(provider) ⇒ Hash?

Get info about a provider.

Parameters:

  • provider (String)

    Provider name

Returns:

  • (Hash, nil)


99
100
101
# File 'lib/prompt_objects/llm/factory.rb', line 99

def provider_info(provider)
  PROVIDERS[provider.to_s.downcase]
end

.providersArray<String>

List available providers.

Returns:

  • (Array<String>)


92
93
94
# File 'lib/prompt_objects/llm/factory.rb', line 92

def providers
  PROVIDERS.keys
end