|
18 | 18 | use PHPUnit\Framework\Attributes\TestWith; |
19 | 19 | use PHPUnit\Framework\TestCase; |
20 | 20 | use Probots\Pinecone\Client as PineconeClient; |
| 21 | +use Psr\Log\LoggerInterface; |
| 22 | +use Psr\Log\NullLogger; |
21 | 23 | use Symfony\AI\Agent\AgentInterface; |
22 | 24 | use Symfony\AI\Agent\Memory\MemoryInputProcessor; |
23 | 25 | use Symfony\AI\Agent\Memory\StaticMemoryProvider; |
|
31 | 33 | use Symfony\AI\Platform\Bridge\ElevenLabs\ElevenLabsApiCatalog; |
32 | 34 | use Symfony\AI\Platform\Bridge\ElevenLabs\ModelCatalog as ElevenLabsModelCatalog; |
33 | 35 | use Symfony\AI\Platform\Bridge\ElevenLabs\PlatformFactory as ElevenLabsPlatformFactory; |
| 36 | +use Symfony\AI\Platform\Bridge\Failover\FailoverPlatform; |
| 37 | +use Symfony\AI\Platform\Bridge\Failover\FailoverPlatformFactory; |
34 | 38 | use Symfony\AI\Platform\Bridge\Ollama\OllamaApiCatalog; |
35 | 39 | use Symfony\AI\Platform\CachedPlatform; |
36 | 40 | use Symfony\AI\Platform\Capability; |
|
84 | 88 | use Symfony\Component\DependencyInjection\Reference; |
85 | 89 | use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
86 | 90 | use Symfony\Component\HttpClient\HttpClient; |
| 91 | +use Symfony\Component\RateLimiter\RateLimiterFactory; |
| 92 | +use Symfony\Component\RateLimiter\Storage\InMemoryStorage; |
87 | 93 | use Symfony\Contracts\HttpClient\HttpClientInterface; |
88 | 94 |
|
89 | 95 | class AiBundleTest extends TestCase |
@@ -4019,6 +4025,62 @@ public function testElevenLabsPlatformWithApiCatalogCanBeRegistered() |
4019 | 4025 | $this->assertSame([['interface' => ModelCatalogInterface::class]], $modelCatalogDefinition->getTag('proxy')); |
4020 | 4026 | } |
4021 | 4027 |
|
| 4028 | + public function testFailoverPlatformCanBeCreated() |
| 4029 | + { |
| 4030 | + $container = $this->buildContainer([ |
| 4031 | + 'ai' => [ |
| 4032 | + 'platform' => [ |
| 4033 | + 'ollama' => [ |
| 4034 | + 'host_url' => 'http://127.0.0.1:11434', |
| 4035 | + ], |
| 4036 | + 'openai' => [ |
| 4037 | + 'api_key' => 'sk-openai_key_full', |
| 4038 | + ], |
| 4039 | + 'failover' => [ |
| 4040 | + 'main' => [ |
| 4041 | + 'platforms' => [ |
| 4042 | + 'ai.platform.ollama', |
| 4043 | + 'ai.platform.openai', |
| 4044 | + ], |
| 4045 | + 'rate_limiter' => 'limiter.failover_platform', |
| 4046 | + ], |
| 4047 | + ], |
| 4048 | + ], |
| 4049 | + ], |
| 4050 | + ]); |
| 4051 | + |
| 4052 | + $this->assertTrue($container->hasDefinition('ai.platform.failover.main')); |
| 4053 | + |
| 4054 | + $definition = $container->getDefinition('ai.platform.failover.main'); |
| 4055 | + |
| 4056 | + $this->assertSame([ |
| 4057 | + FailoverPlatformFactory::class, |
| 4058 | + 'create', |
| 4059 | + ], $definition->getFactory()); |
| 4060 | + $this->assertTrue($definition->isLazy()); |
| 4061 | + $this->assertSame(FailoverPlatform::class, $definition->getClass()); |
| 4062 | + |
| 4063 | + $this->assertCount(4, $definition->getArguments()); |
| 4064 | + $this->assertCount(2, $definition->getArgument(0)); |
| 4065 | + $this->assertEquals([ |
| 4066 | + new Reference('ai.platform.ollama'), |
| 4067 | + new Reference('ai.platform.openai'), |
| 4068 | + ], $definition->getArgument(0)); |
| 4069 | + $this->assertInstanceOf(Reference::class, $definition->getArgument(1)); |
| 4070 | + $this->assertSame('limiter.failover_platform', (string) $definition->getArgument(1)); |
| 4071 | + $this->assertInstanceOf(Reference::class, $definition->getArgument(2)); |
| 4072 | + $this->assertSame(ClockInterface::class, (string) $definition->getArgument(2)); |
| 4073 | + $this->assertInstanceOf(Reference::class, $definition->getArgument(3)); |
| 4074 | + $this->assertSame(LoggerInterface::class, (string) $definition->getArgument(3)); |
| 4075 | + |
| 4076 | + $this->assertTrue($definition->hasTag('proxy')); |
| 4077 | + $this->assertSame([['interface' => PlatformInterface::class]], $definition->getTag('proxy')); |
| 4078 | + $this->assertTrue($definition->hasTag('ai.platform')); |
| 4079 | + $this->assertSame([['name' => 'failover']], $definition->getTag('ai.platform')); |
| 4080 | + |
| 4081 | + $this->assertTrue($container->hasAlias('Symfony\AI\Platform\PlatformInterface $main')); |
| 4082 | + } |
| 4083 | + |
4022 | 4084 | public function testOpenAiPlatformWithDefaultRegion() |
4023 | 4085 | { |
4024 | 4086 | $container = $this->buildContainer([ |
@@ -7013,6 +7075,16 @@ private function buildContainer(array $configuration): ContainerBuilder |
7013 | 7075 | $container->setParameter('kernel.environment', 'dev'); |
7014 | 7076 | $container->setParameter('kernel.build_dir', 'public'); |
7015 | 7077 | $container->setDefinition(ClockInterface::class, new Definition(MonotonicClock::class)); |
| 7078 | + $container->setDefinition(LoggerInterface::class, new Definition(NullLogger::class)); |
| 7079 | + $container->setDefinition('limiter.failover_platform', new Definition(RateLimiterFactory::class, [ |
| 7080 | + [ |
| 7081 | + 'policy' => 'sliding_window', |
| 7082 | + 'id' => 'test', |
| 7083 | + 'interval' => '60 seconds', |
| 7084 | + 'limit' => 1, |
| 7085 | + ], |
| 7086 | + new Definition(InMemoryStorage::class), |
| 7087 | + ])); |
7016 | 7088 |
|
7017 | 7089 | $extension = (new AiBundle())->getContainerExtension(); |
7018 | 7090 | $extension->load($configuration, $container); |
@@ -7068,6 +7140,15 @@ private function getFullConfig(): array |
7068 | 7140 | 'host' => 'https://api.elevenlabs.io/v1', |
7069 | 7141 | 'api_key' => 'elevenlabs_key_full', |
7070 | 7142 | ], |
| 7143 | + 'failover' => [ |
| 7144 | + 'main' => [ |
| 7145 | + 'platforms' => [ |
| 7146 | + 'ai.platform.ollama', |
| 7147 | + 'ai.platform.openai', |
| 7148 | + ], |
| 7149 | + 'rate_limiter' => 'limiter.failover_platform', |
| 7150 | + ], |
| 7151 | + ], |
7071 | 7152 | 'gemini' => [ |
7072 | 7153 | 'api_key' => 'gemini_key_full', |
7073 | 7154 | ], |
|
0 commit comments