Skip to content

Commit cc257db

Browse files
committed
Add tests for OpenRouter platform
🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent 302953a commit cc257db

File tree

3 files changed

+502
-0
lines changed

3 files changed

+502
-0
lines changed
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\AI\Platform\Tests\Bridge\OpenRouter;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\AI\Platform\Bridge\Generic\CompletionsModel;
16+
use Symfony\AI\Platform\Bridge\Generic\EmbeddingsModel;
17+
use Symfony\AI\Platform\Bridge\OpenRouter\ModelApiCatalog;
18+
use Symfony\AI\Platform\Capability;
19+
use Symfony\Component\HttpClient\MockHttpClient;
20+
use Symfony\Component\HttpClient\Response\JsonMockResponse;
21+
22+
/**
23+
* @author Oskar Stark <[email protected]>
24+
*/
25+
final class ModelApiCatalogTest extends TestCase
26+
{
27+
public function testGetModelLoadsFromApi()
28+
{
29+
$httpClient = new MockHttpClient([
30+
new JsonMockResponse([
31+
'data' => [
32+
[
33+
'id' => 'anthropic/claude-3-opus',
34+
'architecture' => [
35+
'input_modalities' => ['text', 'image'],
36+
'output_modalities' => ['text'],
37+
],
38+
],
39+
],
40+
]),
41+
new JsonMockResponse([
42+
'data' => [],
43+
]),
44+
]);
45+
46+
$catalog = new ModelApiCatalog($httpClient);
47+
$model = $catalog->getModel('anthropic/claude-3-opus');
48+
49+
$this->assertSame('anthropic/claude-3-opus', $model->getName());
50+
$this->assertContains(Capability::INPUT_TEXT, $model->getCapabilities());
51+
$this->assertContains(Capability::INPUT_IMAGE, $model->getCapabilities());
52+
$this->assertContains(Capability::OUTPUT_TEXT, $model->getCapabilities());
53+
$this->assertSame(2, $httpClient->getRequestsCount());
54+
}
55+
56+
public function testGetModelsLoadsFromApi()
57+
{
58+
$httpClient = new MockHttpClient([
59+
new JsonMockResponse([
60+
'data' => [
61+
[
62+
'id' => 'openai/gpt-4',
63+
'architecture' => [
64+
'input_modalities' => ['text'],
65+
'output_modalities' => ['text'],
66+
],
67+
],
68+
[
69+
'id' => 'google/gemini-pro-vision',
70+
'architecture' => [
71+
'input_modalities' => ['text', 'image'],
72+
'output_modalities' => ['text'],
73+
],
74+
],
75+
],
76+
]),
77+
new JsonMockResponse([
78+
'data' => [
79+
[
80+
'id' => 'openai/text-embedding-ada-002',
81+
],
82+
],
83+
]),
84+
]);
85+
86+
$catalog = new ModelApiCatalog($httpClient);
87+
$models = $catalog->getModels();
88+
89+
// Should include base models (openrouter/auto, @preset) + API models + embeddings
90+
$this->assertArrayHasKey('openrouter/auto', $models);
91+
$this->assertArrayHasKey('@preset', $models);
92+
$this->assertArrayHasKey('openai/gpt-4', $models);
93+
$this->assertArrayHasKey('google/gemini-pro-vision', $models);
94+
$this->assertArrayHasKey('openai/text-embedding-ada-002', $models);
95+
96+
$this->assertSame(CompletionsModel::class, $models['openai/gpt-4']['class']);
97+
$this->assertSame(CompletionsModel::class, $models['google/gemini-pro-vision']['class']);
98+
$this->assertSame(EmbeddingsModel::class, $models['openai/text-embedding-ada-002']['class']);
99+
100+
$this->assertSame(2, $httpClient->getRequestsCount());
101+
}
102+
103+
public function testModelsAreOnlyLoadedOnce()
104+
{
105+
$httpClient = new MockHttpClient([
106+
new JsonMockResponse([
107+
'data' => [
108+
[
109+
'id' => 'test/model',
110+
'architecture' => [
111+
'input_modalities' => ['text'],
112+
'output_modalities' => ['text'],
113+
],
114+
],
115+
],
116+
]),
117+
new JsonMockResponse([
118+
'data' => [],
119+
]),
120+
]);
121+
122+
$catalog = new ModelApiCatalog($httpClient);
123+
124+
// Call getModels twice
125+
$catalog->getModels();
126+
$catalog->getModels();
127+
128+
// Should only make 2 API calls total (models + embeddings), not 4
129+
$this->assertSame(2, $httpClient->getRequestsCount());
130+
}
131+
132+
public function testGetModelWithAudioInputModality()
133+
{
134+
$httpClient = new MockHttpClient([
135+
new JsonMockResponse([
136+
'data' => [
137+
[
138+
'id' => 'openai/whisper',
139+
'architecture' => [
140+
'input_modalities' => ['audio'],
141+
'output_modalities' => ['text'],
142+
],
143+
],
144+
],
145+
]),
146+
new JsonMockResponse([
147+
'data' => [],
148+
]),
149+
]);
150+
151+
$catalog = new ModelApiCatalog($httpClient);
152+
$model = $catalog->getModel('openai/whisper');
153+
154+
$this->assertContains(Capability::INPUT_AUDIO, $model->getCapabilities());
155+
$this->assertContains(Capability::OUTPUT_TEXT, $model->getCapabilities());
156+
}
157+
158+
public function testGetModelWithFileInputModality()
159+
{
160+
$httpClient = new MockHttpClient([
161+
new JsonMockResponse([
162+
'data' => [
163+
[
164+
'id' => 'anthropic/claude-pdf',
165+
'architecture' => [
166+
'input_modalities' => ['text', 'file'],
167+
'output_modalities' => ['text'],
168+
],
169+
],
170+
],
171+
]),
172+
new JsonMockResponse([
173+
'data' => [],
174+
]),
175+
]);
176+
177+
$catalog = new ModelApiCatalog($httpClient);
178+
$model = $catalog->getModel('anthropic/claude-pdf');
179+
180+
$this->assertContains(Capability::INPUT_TEXT, $model->getCapabilities());
181+
$this->assertContains(Capability::INPUT_PDF, $model->getCapabilities());
182+
$this->assertContains(Capability::OUTPUT_TEXT, $model->getCapabilities());
183+
}
184+
185+
public function testGetModelWithVideoInputModality()
186+
{
187+
$httpClient = new MockHttpClient([
188+
new JsonMockResponse([
189+
'data' => [
190+
[
191+
'id' => 'google/gemini-video',
192+
'architecture' => [
193+
'input_modalities' => ['text', 'video'],
194+
'output_modalities' => ['text'],
195+
],
196+
],
197+
],
198+
]),
199+
new JsonMockResponse([
200+
'data' => [],
201+
]),
202+
]);
203+
204+
$catalog = new ModelApiCatalog($httpClient);
205+
$model = $catalog->getModel('google/gemini-video');
206+
207+
$this->assertContains(Capability::INPUT_TEXT, $model->getCapabilities());
208+
$this->assertContains(Capability::INPUT_MULTIMODAL, $model->getCapabilities());
209+
$this->assertContains(Capability::OUTPUT_TEXT, $model->getCapabilities());
210+
}
211+
212+
public function testGetModelWithImageOutputModality()
213+
{
214+
$httpClient = new MockHttpClient([
215+
new JsonMockResponse([
216+
'data' => [
217+
[
218+
'id' => 'openai/dall-e',
219+
'architecture' => [
220+
'input_modalities' => ['text'],
221+
'output_modalities' => ['image'],
222+
],
223+
],
224+
],
225+
]),
226+
new JsonMockResponse([
227+
'data' => [],
228+
]),
229+
]);
230+
231+
$catalog = new ModelApiCatalog($httpClient);
232+
$model = $catalog->getModel('openai/dall-e');
233+
234+
$this->assertContains(Capability::INPUT_TEXT, $model->getCapabilities());
235+
$this->assertContains(Capability::OUTPUT_IMAGE, $model->getCapabilities());
236+
}
237+
238+
public function testPresetModelStillWorksWithApiCatalog()
239+
{
240+
$httpClient = new MockHttpClient([
241+
new JsonMockResponse([
242+
'data' => [],
243+
]),
244+
new JsonMockResponse([
245+
'data' => [],
246+
]),
247+
]);
248+
249+
$catalog = new ModelApiCatalog($httpClient);
250+
$model = $catalog->getModel('@preset/my-preset');
251+
252+
$this->assertSame('@preset/my-preset', $model->getName());
253+
$this->assertSame(Capability::cases(), $model->getCapabilities());
254+
}
255+
256+
public function testAutoRouterModelStillWorksWithApiCatalog()
257+
{
258+
$httpClient = new MockHttpClient([
259+
new JsonMockResponse([
260+
'data' => [],
261+
]),
262+
new JsonMockResponse([
263+
'data' => [],
264+
]),
265+
]);
266+
267+
$catalog = new ModelApiCatalog($httpClient);
268+
$model = $catalog->getModel('openrouter/auto');
269+
270+
$this->assertSame('openrouter/auto', $model->getName());
271+
$this->assertSame(Capability::cases(), $model->getCapabilities());
272+
}
273+
}

0 commit comments

Comments
 (0)