Module: Rubino::Memory::AuxRetry
- Included in:
- Backends::Sqlite
- Defined in:
- lib/rubino/memory/aux_retry.rb
Overview
Bounded retry/backoff for the aux memory-extraction call (r5 C-2).
The aux client calls the adapter directly and so — unlike the main conversation loop, whose Agent::ModelCallRunner owns retry/backoff — got NO retry: under concurrent load a single RubyLLM::RateLimitError (429) was caught at the call site, logged ‘memory.sqlite.skip`, and the extracted fact was DROPPED for good. This mixin wraps the aux call in the SAME jittered-backoff policy the main loop uses, retrying retryable errors (429/overloaded/5xx/transport, per LLM::ErrorClassifier) up to a small budget and honouring Retry-After on a rate-limit. After the budget is exhausted (or on a non-retryable error) it re-raises to the caller, which leaves the per-session cursor put so the turn is re-fed next time rather than silently lost.
Host requirements: ‘@config` (a Config::Configuration answering #dig) and a `DEFAULT_EXTRACT_MAX_RETRIES` constant on the including class.
Instance Method Summary collapse
-
#with_aux_retry ⇒ Object
Run ‘block` (the aux call), retrying transient errors.
Instance Method Details
#with_aux_retry ⇒ Object
Run ‘block` (the aux call), retrying transient errors. Re-raises the last error once the budget is exhausted or the error is non-retryable.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/rubino/memory/aux_retry.rb', line 24 def with_aux_retry attempts = 0 begin # Honour a detached-polishing cancel (Esc to skip): the background # housekeeping thread binds Rubino.aux_cancel_token, so an Esc that # cancelled it must abort BEFORE spending another aux-LLM call rather # than running to completion off-screen (#319). aux_check_cancelled! yield rescue Rubino::Interrupted # Cancellation is terminal — re-raise straight through so the detached # polishing thread unwinds and leaves the cursor put (re-runs next turn). raise rescue StandardError => e classified = LLM::ErrorClassifier.classify(e) raise unless classified.retryable && attempts < extract_max_retries attempts += 1 wait = aux_backoff.wait_seconds( attempts, base: Agent::BackoffPolicy::ERROR_PATH[:base], max: Agent::BackoffPolicy::ERROR_PATH[:max], retry_after: aux_rate_limit_retry_after(classified, e) ) log_aux_retry(e, attempts, wait) # Sleep in short slices so an Esc during the (possibly long, Retry-After # honouring) backoff wait aborts within ~100ms instead of holding the # detached worker for the full window (#319). On the foreground/API # path no token is bound, so this is one uninterrupted sleep as before. aux_cancellable_sleep(wait) retry end end |