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) {