openrouter: delegate model caching and 429 retry to provider plugin AiProvidersInfoCache (6fb05aa) memoizes getModels(), and AiHttpClient's ServiceUnavailableRetryStrategy (6b4d89b) retries 429. Drop the script-level supplier memoization, 404 invalidation, manual 429 retry loop and rateLimitMessage helper. Change-Id: I0b311a3f6787f070bd821cf5878ada4fe22a6aa1 (cherry picked from commit d5b7f223f0abdaa335718b1911cad9a774184439)
diff --git a/ai/ai-review-agent-openrouter-1.0.groovy b/ai/ai-review-agent-openrouter-1.0.groovy index 30f75f3..7cf579d 100644 --- a/ai/ai-review-agent-openrouter-1.0.groovy +++ b/ai/ai-review-agent-openrouter-1.0.groovy
@@ -14,8 +14,6 @@ import com.gerritforge.gerrit.plugins.ai.provider.api.* -import com.google.common.base.Supplier -import com.google.common.base.Suppliers import com.google.common.flogger.FluentLogger import com.google.gerrit.extensions.registration.DynamicSet import com.google.inject.* @@ -25,7 +23,6 @@ import org.apache.http.entity.StringEntity import java.nio.charset.StandardCharsets -import java.util.concurrent.TimeUnit import groovy.json.* @@ -38,10 +35,6 @@ private static final String ATTRIBUTION_TITLE = 'Gerrit AI Review' private static final int MAX_ERROR_LEN = 500 - // Single flat-window retry on 429 (AiHttpClient hides Retry-After). - private static final int MAX_RETRIES = 1 - private static final long RETRY_WAIT_MS = 5_000L - // Floating aliases that always resolve to the latest vendor version. // DeepSeek (no `~latest` alias) is sourced dynamically below. private static final List<String> PAID_LATEST_MODELS = [ @@ -55,29 +48,15 @@ ] private static final int FREE_PICK_COUNT = 5 - private static final long CACHE_TTL_DAYS = 1L final String displayName = 'OpenRouter' @Inject private AiHttpClient http - // Refreshed once per TTL, or when review() sees a 404 (slug gone upstream). - private volatile Supplier<LinkedHashSet<String>> modelsSupplier = newModelsSupplier() - - private Supplier<LinkedHashSet<String>> newModelsSupplier() { - return Suppliers.memoizeWithExpiration( - { -> fetchAndMergeModels() } as Supplier, - CACHE_TTL_DAYS, TimeUnit.DAYS) - } - @Override Set<String> getModels(String notUsed) { - return modelsSupplier.get() - } - - // On failure, return paid-only fallback (also memoized for the TTL). - private LinkedHashSet<String> fetchAndMergeModels() { + // On failure, return paid-only fallback. Caching handled by AiProvidersInfoCache. try { List<Map> catalog = http.get( OPENROUTER_MODELS_URL, @@ -115,49 +94,17 @@ ]).toString(), StandardCharsets.UTF_8) - for (int attempt = 0; attempt <= MAX_RETRIES; attempt++) { - try { - return http.post( - OPENROUTER_API_URL, - headers, - entity, - { extractErrorMessage(it) }, - { extractResponseText(it) }) as String - } catch (JsonException | IOException e) { - String msg = e.message ?: '' - if (msg.contains('[429]')) { - if (attempt < MAX_RETRIES) { - logger.atInfo().log( - 'OpenRouter 429 for model=%s, retrying after %d ms', - model, RETRY_WAIT_MS) - try { - Thread.sleep(RETRY_WAIT_MS) - } catch (InterruptedException ie) { - Thread.currentThread().interrupt() - throw new IllegalStateException( - 'Interrupted while retrying OpenRouter call', ie) - } - continue - } - logger.atInfo().log('OpenRouter 429 exhausted for model=%s', model) - return rateLimitMessage(model) - } - if (msg.contains('[404]')) { - logger.atInfo().log( - 'OpenRouter 404 for model=%s; invalidating catalog cache', model) - modelsSupplier = newModelsSupplier() - } - logger.atWarning().withCause(e).log('Failed to call OpenRouter API (model=%s)', model) - throw new IllegalStateException('Failed to call OpenRouter API', e) - } + try { + return http.post( + OPENROUTER_API_URL, + headers, + entity, + { extractErrorMessage(it) }, + { extractResponseText(it) }) as String + } catch (JsonException | IOException e) { + logger.atWarning().withCause(e).log('Failed to call OpenRouter API (model=%s)', model) + throw new IllegalStateException('Failed to call OpenRouter API', e) } - throw new IllegalStateException('OpenRouter API: retry budget exhausted') - } - - private static String rateLimitMessage(String model) { - return "\n⚠️ **OpenRouter rate limit**\n\n" + - "Model `$model` is temporarily rate-limited by the upstream provider. " + - "Try again shortly, or pick a different model from the list." } private static String extractResponseText(String body) {