PluginProbe
Auto Alt Text / 2.8.2
Auto Alt Text v2.8.2
3.0.3 2.8.2 1.3.1 1.3.2 2.0.0 2.1.0 2.1.1 2.2.0 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.6.1 2.7.0 2.8.0 2.8.1 All 28 releases
auto-alt-text / src / App / AIProviders / Anthropic / AnthropicModelsRegistry.php

AnthropicModelsRegistry.php in Auto Alt Text 2.8.2, at src/App/AIProviders/Anthropic/AnthropicModelsRegistry.php

191 lines 5.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace AATXT\App\AIProviders\Anthropic;
6
7 use AATXT\App\Infrastructure\Cache\CacheInterface;
8 use AATXT\App\Infrastructure\Http\HttpClientInterface;
9 use AATXT\Config\Constants;
10 use Exception;
11
12 /**
13 * Registry that exposes the list of Claude models currently available on the
14 * Anthropic account, fetched from the public /v1/models endpoint and cached
15 * via the injected cache backend.
16 *
17 * On any failure (missing API key, HTTP error, empty/malformed response) it
18 * falls back to the static list in Constants so the plugin keeps working.
19 */
20 class AnthropicModelsRegistry
21 {
22 /**
23 * Cache TTL for a successful response (24 hours).
24 */
25 private const CACHE_TTL_SUCCESS = 86400;
26
27 /**
28 * Cache TTL applied to the fallback list after a fetch failure (1 hour),
29 * so we do not hammer the API while Anthropic is unreachable.
30 */
31 private const CACHE_TTL_FALLBACK = 3600;
32
33 /**
34 * @var HttpClientInterface
35 */
36 private $httpClient;
37
38 /**
39 * @var CacheInterface
40 */
41 private $cache;
42
43 /**
44 * @var string Decrypted Anthropic API key, possibly empty.
45 */
46 private $apiKey;
47
48 /**
49 * @var array<string,string>|null In-request memoization.
50 */
51 private $memoizedModels = null;
52
53 public function __construct(
54 HttpClientInterface $httpClient,
55 CacheInterface $cache,
56 string $apiKey
57 ) {
58 $this->httpClient = $httpClient;
59 $this->cache = $cache;
60 $this->apiKey = $apiKey;
61 }
62
63 public function hasApiKey(): bool
64 {
65 return $this->apiKey !== '';
66 }
67
68 /**
69 * Return the available models as `id => display_name`, ordered with the
70 * most recent first. Falls back to the static list on any failure.
71 *
72 * @return array<string,string>
73 */
74 public function getAvailableModels(): array
75 {
76 if ($this->memoizedModels !== null) {
77 return $this->memoizedModels;
78 }
79
80 $cached = $this->cache->get(Constants::AATXT_ANTHROPIC_MODELS_CACHE_KEY);
81 if (is_array($cached)) {
82 return $this->memoizedModels = $cached;
83 }
84
85 if (!$this->hasApiKey()) {
86 return $this->memoizedModels = $this->fallbackList();
87 }
88
89 try {
90 $models = $this->fetchFromApi();
91 } catch (Exception $e) {
92 $fallback = $this->fallbackList();
93 $this->cache->set(
94 Constants::AATXT_ANTHROPIC_MODELS_CACHE_KEY,
95 $fallback,
96 self::CACHE_TTL_FALLBACK
97 );
98 return $this->memoizedModels = $fallback;
99 }
100
101 if (empty($models)) {
102 return $this->memoizedModels = $this->fallbackList();
103 }
104
105 $this->cache->set(
106 Constants::AATXT_ANTHROPIC_MODELS_CACHE_KEY,
107 $models,
108 self::CACHE_TTL_SUCCESS
109 );
110 return $this->memoizedModels = $models;
111 }
112
113 public function isAvailable(string $modelId): bool
114 {
115 if ($modelId === '') {
116 return false;
117 }
118 return array_key_exists($modelId, $this->getAvailableModels());
119 }
120
121 /**
122 * Return the most recent model id from the available list, used as
123 * runtime fallback when the configured model is no longer available.
124 */
125 public function getDefaultModel(): string
126 {
127 $models = $this->getAvailableModels();
128 if (empty($models)) {
129 return Constants::AATXT_CLAUDE_FALLBACK_MODEL;
130 }
131 return (string) array_key_first($models);
132 }
133
134 public function flushCache(): void
135 {
136 $this->cache->delete(Constants::AATXT_ANTHROPIC_MODELS_CACHE_KEY);
137 $this->memoizedModels = null;
138 }
139
140 /**
141 * @return array<string,string>
142 * @throws Exception
143 */
144 private function fetchFromApi(): array
145 {
146 $headers = [
147 'x-api-key' => $this->apiKey,
148 'anthropic-version' => Constants::AATXT_API_VERSION,
149 ];
150
151 $data = $this->httpClient->get(Constants::AATXT_ANTHROPIC_MODELS_ENDPOINT, $headers);
152 $items = isset($data['data']) && is_array($data['data']) ? $data['data'] : [];
153
154 $items = array_values(array_filter($items, function ($item) {
155 if (!is_array($item)) {
156 return false;
157 }
158 $id = isset($item['id']) ? (string) $item['id'] : '';
159 if ($id === '') {
160 return false;
161 }
162 return !preg_match('/^claude-(2|instant)/i', $id);
163 }));
164
165 usort($items, function ($a, $b) {
166 $aCreated = isset($a['created_at']) ? (string) $a['created_at'] : '';
167 $bCreated = isset($b['created_at']) ? (string) $b['created_at'] : '';
168 return strcmp($bCreated, $aCreated);
169 });
170
171 $result = [];
172 foreach ($items as $item) {
173 $id = (string) $item['id'];
174 $display = isset($item['display_name']) && $item['display_name'] !== ''
175 ? (string) $item['display_name']
176 : $id;
177 $result[$id] = $display;
178 }
179
180 return $result;
181 }
182
183 /**
184 * @return array<string,string>
185 */
186 private function fallbackList(): array
187 {
188 return Constants::AATXT_OPTION_FIELD_MODEL_ANTHROPIC_OPTIONS;
189 }
190 }
191