Class: OllamaAgent::Providers::Credential

Inherits:
Object
  • Object
show all
Defined in:
lib/ollama_agent/providers/credential.rb

Overview

Runtime credential resource.

A Credential wraps an API key (and optional base_url for compatible APIs) together with its live health state: failures, cooldowns, quota consumption, and a permanent-disable flag for authentication failures.

All mutable state is protected by a Mutex so that the CredentialRouter can safely access credentials from concurrent threads.

Examples:

Configuring a credential

cred = OllamaAgent::Providers::Credential.new(
  id:       "openai-primary",
  provider: "openai",
  api_key:  ENV["OPENAI_KEY_1"],
  weight:   2,
  limits:   { rpm: 60, tpm: 90_000, daily_tokens: 10_000_000 }
)
cred.available?   # => true
cred.mark_failure!(OllamaAgent::RateLimitError.new("429"))
cred.available?   # => false (cooling down)

Constant Summary collapse

COOLDOWNS =

Cooldown durations by error type

{
  OllamaAgent::QuotaExhaustedError => 3600, # 1 h  — daily/monthly limit
  OllamaAgent::RateLimitError => 60, # 60 s — RPM/TPM window
  OllamaAgent::TemporaryProviderError => 15 # 15 s — 5xx / timeout
}.freeze
DEFAULT_COOLDOWN =

s — unknown errors

30

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id:, provider:, api_key: nil, base_url: nil, weight: 1, limits: {}, name: nil) ⇒ Credential

Returns a new instance of Credential.

Parameters:

  • id (String)

    unique name, e.g. “openai-key-1”

  • provider (String)

    “openai” | “anthropic” | “ollama” | “groq” | …

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

    override for compatible endpoints (Groq, Together, etc.)

  • weight (Integer) (defaults to: 1)

    relative routing weight (1–100); higher = more traffic

  • limits (Hash) (defaults to: {})

    { rpm:, tpm:, daily_tokens:, daily_requests: }

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

    human-readable label; defaults to id



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/ollama_agent/providers/credential.rb', line 47

def initialize(id:, provider:, api_key: nil, base_url: nil,
               weight: 1, limits: {}, name: nil)
  @id            = id.to_s
  @provider      = provider.to_s
  @api_key       = api_key
  @base_url      = base_url
  @weight        = weight.to_i.clamp(1, 100)
  @limits        = limits.transform_keys(&:to_sym)
  @display_name  = name || id.to_s
  @quota_tracker = QuotaTracker.new(limits: @limits)

  # ── Mutable health state (Mutex-protected) ──────────────────────
  @mutex          = Mutex.new
  @failures       = 0
  @cooldown_until = nil
  @disabled       = false # permanent — set on AuthenticationError
  @last_used_at   = nil
end

Instance Attribute Details

#api_keyObject (readonly)

Returns the value of attribute api_key.



37
38
39
# File 'lib/ollama_agent/providers/credential.rb', line 37

def api_key
  @api_key
end

#base_urlObject (readonly)

Returns the value of attribute base_url.



37
38
39
# File 'lib/ollama_agent/providers/credential.rb', line 37

def base_url
  @base_url
end

#idObject (readonly)

Returns the value of attribute id.



37
38
39
# File 'lib/ollama_agent/providers/credential.rb', line 37

def id
  @id
end

#limitsObject (readonly)

Returns the value of attribute limits.



37
38
39
# File 'lib/ollama_agent/providers/credential.rb', line 37

def limits
  @limits
end

#providerObject (readonly)

Returns the value of attribute provider.



37
38
39
# File 'lib/ollama_agent/providers/credential.rb', line 37

def provider
  @provider
end

#quota_trackerObject (readonly)

Returns the value of attribute quota_tracker.



37
38
39
# File 'lib/ollama_agent/providers/credential.rb', line 37

def quota_tracker
  @quota_tracker
end

#weightObject (readonly)

Returns the value of attribute weight.



37
38
39
# File 'lib/ollama_agent/providers/credential.rb', line 37

def weight
  @weight
end

Instance Method Details

#available?Boolean

True when this credential can currently accept a request.

Returns:

  • (Boolean)


68
69
70
# File 'lib/ollama_agent/providers/credential.rb', line 68

def available?
  @mutex.synchronize { available_unsafe? }
end

#cooldown_remainingInteger

Returns remaining cooldown in seconds (0 if not cooling down).

Returns:

  • (Integer)


107
108
109
110
111
112
113
# File 'lib/ollama_agent/providers/credential.rb', line 107

def cooldown_remaining
  @mutex.synchronize do
    return 0 unless @cooldown_until && Time.now < @cooldown_until

    (@cooldown_until - Time.now).ceil
  end
end

#mark_failure!(error) ⇒ Object

Record a failure and apply the appropriate cooldown / permanent disable.

Parameters:

  • error (StandardError)

    ideally a typed OllamaAgent error



81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/ollama_agent/providers/credential.rb', line 81

def mark_failure!(error)
  @mutex.synchronize do
    if error.is_a?(OllamaAgent::AuthenticationError)
      @disabled = true # permanent — bad key, never retry
      return
    end

    @failures += 1
    cooldown_seconds = COOLDOWNS.fetch(error.class, DEFAULT_COOLDOWN)
    @cooldown_until  = Time.now + cooldown_seconds
  end
end

#mark_success!(usage: nil) ⇒ Object

Record a successful response and update quota state.

Parameters:

  • usage (Hash, nil) (defaults to: nil)

    token usage from the response



96
97
98
99
100
101
102
103
# File 'lib/ollama_agent/providers/credential.rb', line 96

def mark_success!(usage: nil)
  @mutex.synchronize do
    @failures       = 0
    @cooldown_until = nil
    @last_used_at   = Time.now
  end
  @quota_tracker.record(usage) if usage
end

#near_exhaustion?Boolean

True when usage is approaching the daily token limit (>= 90%). Used for predictive rerouting before a hard failure.

Returns:

  • (Boolean)


75
76
77
# File 'lib/ollama_agent/providers/credential.rb', line 75

def near_exhaustion?
  @quota_tracker.near_exhaustion?
end

#status_summaryHash

Full status snapshot for TUI and telemetry.

Returns:

  • (Hash)


117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/ollama_agent/providers/credential.rb', line 117

def status_summary
  @mutex.synchronize do
    {
      id: @id,
      provider: @provider,
      name: @display_name,
      available: available_unsafe?,
      disabled: @disabled,
      cooling_down: cooling_down?,
      cooldown_secs: cooldown_remaining_unsafe,
      failures: @failures,
      last_used_at: @last_used_at,
      near_exhaustion: near_exhaustion?,
      quota: @quota_tracker.summary
    }
  end
end

#to_sObject



135
136
137
# File 'lib/ollama_agent/providers/credential.rb', line 135

def to_s
  "#<#{self.class.name} id=#{@id} provider=#{@provider}>"
end