Class: KairosMcp::Daemon::DaemonLlmCaller
- Inherits:
-
Object
- Object
- KairosMcp::Daemon::DaemonLlmCaller
- Defined in:
- lib/kairos_mcp/daemon/daemon_llm_caller.rb
Overview
DaemonLlmCaller โ thin Anthropic API wrapper for daemon LLM phases.
Design (Phase 4 v0.3 ยง1.3):
Interface contract (LlmPhaseFunctions expects):
caller.call(messages:, system:, max_tokens:, **) โ Hash
Return: { content: String, input_tokens: Int, output_tokens: Int,
attempts: Int }
Usage tracking contract:
`attempts` reports TOTAL HTTP requests made (including retries).
`input_tokens` / `output_tokens` are from the SUCCESSFUL response only.
UsageAccumulator records `attempts` as llm_calls (not 1).
Shutdown contract:
Caller injects `stop_requested:` proc. DaemonLlmCaller checks it
between retry sleeps and before each HTTP call.
Defined Under Namespace
Classes: ConfigError, LlmCallError, ShutdownRequested
Constant Summary collapse
- API_URL =
'https://api.anthropic.com/v1/messages'- API_VERSION =
'2023-06-01'- DEFAULT_MODEL =
'claude-sonnet-4-6-20260514'- DEFAULT_TIMEOUT =
short for SIGTERM compliance (<10s)
5- MAX_RETRIES =
2- MAX_RETRY_SLEEP =
fallback cap when no Retry-After header
30
Instance Method Summary collapse
-
#call(messages:, system:, max_tokens:) ⇒ Hash
{ content:, input_tokens:, output_tokens:, attempts: }.
-
#initialize(api_key:, model: DEFAULT_MODEL, timeout: DEFAULT_TIMEOUT, stop_requested: -> { false }, heartbeat_callback: nil, logger: nil) ⇒ DaemonLlmCaller
constructor
A new instance of DaemonLlmCaller.
-
#verify! ⇒ Object
Startup probe: verify API key is valid.
Constructor Details
#initialize(api_key:, model: DEFAULT_MODEL, timeout: DEFAULT_TIMEOUT, stop_requested: -> { false }, heartbeat_callback: nil, logger: nil) ⇒ DaemonLlmCaller
Returns a new instance of DaemonLlmCaller.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/kairos_mcp/daemon/daemon_llm_caller.rb', line 54 def initialize( api_key:, model: DEFAULT_MODEL, timeout: DEFAULT_TIMEOUT, stop_requested: -> { false }, heartbeat_callback: nil, logger: nil ) @api_key = api_key @model = model @timeout = timeout @stop_requested = stop_requested @heartbeat_callback = heartbeat_callback @logger = logger validate_config! end |
Instance Method Details
#call(messages:, system:, max_tokens:) ⇒ Hash
Returns { content:, input_tokens:, output_tokens:, attempts: }.
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/kairos_mcp/daemon/daemon_llm_caller.rb', line 77 def call(messages:, system:, max_tokens:, **) attempts = 0 last_error = nil (1 + MAX_RETRIES).times do |i| check_shutdown! attempts += 1 call_start = monotonic_now begin response = http_post( model: @model, max_tokens: max_tokens, system: system, messages: ) log_call(:info, attempts, call_start, response) return { content: extract_text(response), input_tokens: response.dig('usage', 'input_tokens') || 0, output_tokens: response.dig('usage', 'output_tokens') || 0, attempts: attempts } rescue LlmCallError => e last_error = e log(:warn, "LLM call failed: HTTP #{e.http_code} (attempt #{i + 1}/#{1 + MAX_RETRIES})") if e.retryable && i < MAX_RETRIES sleep_time = compute_backoff(i, e) log(:info, "Retrying after #{sleep_time}s") interruptible_sleep(sleep_time) else break end end end # Attach total attempt count to the error for budget tracking if last_error last_error.instance_variable_set(:@attempts, attempts) unless last_error.attempts > 0 raise last_error end raise LlmCallError.new('unknown error', attempts: attempts) end |
#verify! ⇒ Object
Startup probe: verify API key is valid.
128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/kairos_mcp/daemon/daemon_llm_caller.rb', line 128 def verify! call( messages: [{ role: 'user', content: 'ping' }], system: 'Reply with exactly "pong".', max_tokens: 8 ) true rescue LlmCallError => e raise ConfigError, "API key verification failed: #{e.}" rescue ShutdownRequested # Shutdown during verify is fine โ don't mask as ConfigError raise end |