Class: Esp::Providers::Ollama

Inherits:
OpenAI
  • Object
show all
Defined in:
lib/esp/providers/ollama.rb

Overview

Ollama (local-runtime) provider. Talks to Ollama’s OpenAI-compatible Chat Completions endpoint (‘<base>/v1/chat/completions`), so it reuses the OpenAI provider’s request/response translation in full — the only differences are the base URL, a placeholder API key (the openai gem requires one but Ollama ignores it), and a reachability probe in place of “key is set” for the ‘configured?` semantic.

‘auto_default: false` at registration time means Ollama is never picked automatically — the user selects it explicitly in the UI, so a reachable local runtime doesn’t mask an un-keyed cloud provider.

Constant Summary collapse

DEFAULT_MODEL =
'llama3.2'.freeze
DEFAULT_BASE_URL =
'http://localhost:11434/v1'.freeze
ENV_KEY =
'OLLAMA_BASE_URL'.freeze
PROBE_TIMEOUT =

seconds — keep snappy, the UI hits this on /providers

0.25
PROBE_TTL =

cache reachability so refreshes don’t hammer Ollama

3.0

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from OpenAI

#complete, #initialize

Constructor Details

This class inherits a constructor from Esp::Providers::OpenAI

Class Method Details

.configured?Boolean

‘configured?` = reachable, not “env set” — the base URL has a default, so the var being unset is the common case rather than misconfigured.

Returns:

  • (Boolean)


32
33
34
# File 'lib/esp/providers/ollama.rb', line 32

def configured?
  reachable?
end

.list_models(base_url: nil) ⇒ Object

GET /api/tags → list of installed models for the model-input autocomplete (slice 3). Returns [] on any failure — auto-discovery is convenience, not capability; if Ollama is down or slow the user still types the model name by hand.



60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/esp/providers/ollama.rb', line 60

def list_models(base_url: nil)
  url = base_url || ENV.fetch(ENV_KEY, DEFAULT_BASE_URL)
  uri = URI("#{url.sub(%r{/v1/?\z}, '')}/api/tags")
  res = Net::HTTP.start(uri.host, uri.port, open_timeout: PROBE_TIMEOUT,
                                            read_timeout: 2.0) do |http|
    http.get(uri.path.empty? ? '/api/tags' : uri.path)
  end
  return [] unless res.is_a?(Net::HTTPSuccess)

  (JSON.parse(res.body)['models'] || []).map { |m| m['name'] }.compact
rescue StandardError
  []
end

.reachable?(base_url: nil) ⇒ Boolean

GET /api/version against the configured base, with a tight timeout and a short-lived cache. Returns true iff Ollama answered 2xx.

Returns:

  • (Boolean)


38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/esp/providers/ollama.rb', line 38

def reachable?(base_url: nil)
  url = base_url || ENV.fetch(ENV_KEY, DEFAULT_BASE_URL)
  @probe_mutex.synchronize do
    now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
    cached = @probe_cache[url]
    return cached[:value] if cached && now - cached[:at] < PROBE_TTL

    value = probe(url)
    @probe_cache[url] = { at: now, value: value }
    value
  end
end

.reset_probe_cache!Object

Cleared between tests so caching doesn’t leak between scenarios.



52
53
54
# File 'lib/esp/providers/ollama.rb', line 52

def reset_probe_cache!
  @probe_mutex.synchronize { @probe_cache.clear }
end

Instance Method Details

#build_default_clientObject



89
90
91
92
93
94
95
96
# File 'lib/esp/providers/ollama.rb', line 89

def build_default_client
  require 'openai'
  ::OpenAI::Client.new(
    base_url: ENV.fetch(ENV_KEY, DEFAULT_BASE_URL),
    # The openai gem requires a non-nil api_key; Ollama ignores it.
    api_key: 'ollama'
  )
end