Skip to content

Commit c1bcf91

Browse files
committed
feat(store): ManagedStoreInterface commands added
1 parent 48db6ae commit c1bcf91

File tree

9 files changed

+489
-3
lines changed

9 files changed

+489
-3
lines changed

src/ai-bundle/config/console.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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\Component\DependencyInjection\Loader\Configurator;
13+
14+
use Symfony\AI\Store\Command\DropStoreCommand;
15+
use Symfony\AI\Store\Command\SetupStoreCommand;
16+
17+
return static function (ContainerConfigurator $container): void {
18+
$container->services()
19+
->set('console.command.ai.setup_store', SetupStoreCommand::class)
20+
->args([
21+
service('ai.store_locator'),
22+
[], // Store names
23+
])
24+
->tag('console.command')
25+
->set('console.command.ai.drop_store', DropStoreCommand::class)
26+
->args([
27+
service('ai.store_locator'),
28+
[], // Store names
29+
])
30+
->tag('console.command')
31+
;
32+
};

src/ai-bundle/config/services.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use Symfony\AI\Platform\Contract;
3232
use Symfony\AI\Platform\Contract\JsonSchema\DescriptionParser;
3333
use Symfony\AI\Platform\Contract\JsonSchema\Factory as SchemaFactory;
34+
use Symfony\Component\DependencyInjection\ServiceLocator;
3435

3536
return static function (ContainerConfigurator $container): void {
3637
$container->services()
@@ -131,5 +132,12 @@
131132
service('.inner'),
132133
])
133134
->tag('ai.traceable_toolbox')
135+
136+
// Discovery
137+
->set('ai.store_locator', ServiceLocator::class)
138+
->args([
139+
[],
140+
])
141+
->tag('container.service_locator')
134142
;
135143
};

src/ai-bundle/src/AiBundle.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public function configure(DefinitionConfigurator $definition): void
8989
public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
9090
{
9191
$container->import('../config/services.php');
92+
$container->import('../config/console.php');
9293

9394
foreach ($config['platform'] ?? [] as $type => $platform) {
9495
$this->processPlatformConfig($type, $platform, $builder);
@@ -115,9 +116,18 @@ public function loadExtension(array $config, ContainerConfigurator $container, C
115116
foreach ($config['store'] ?? [] as $type => $store) {
116117
$this->processStoreConfig($type, $store, $builder);
117118
}
119+
118120
$stores = array_keys($builder->findTaggedServiceIds('ai.store'));
119-
if (1 === \count($stores)) {
120-
$builder->setAlias(StoreInterface::class, reset($stores));
121+
122+
if ([] !== $stores) {
123+
$references = [];
124+
foreach ($stores as $storeName) {
125+
$references[$storeName] = new Reference($storeName);
126+
}
127+
128+
$builder->getDefinition('ai.store_locator')->replaceArgument(0, $references);
129+
$builder->getDefinition('console.command.ai.setup_store')->replaceArgument(0, $stores);
130+
$builder->getDefinition('console.command.ai.drop_store')->replaceArgument(0, $stores);
121131
}
122132

123133
foreach ($config['indexer'] as $indexerName => $indexer) {
@@ -507,6 +517,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
507517
->setArguments($arguments);
508518

509519
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
520+
$container->registerAliasForArgument('ai.store.'.$name, StoreInterface::class, (new Target($name.'Store'))->getParsedName());
510521
}
511522
}
512523

@@ -538,6 +549,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
538549
->setArguments($arguments);
539550

540551
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
552+
$container->registerAliasForArgument('ai.store.'.$name, StoreInterface::class, (new Target($name.'Store'))->getParsedName());
541553
}
542554
}
543555

@@ -552,6 +564,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
552564
->addTag('ai.store');
553565

554566
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
567+
$container->registerAliasForArgument('ai.store.'.$name, StoreInterface::class, (new Target($name.'Store'))->getParsedName());
555568
}
556569
}
557570

@@ -578,6 +591,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
578591
;
579592

580593
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
594+
$container->registerAliasForArgument('ai.store.'.$name, StoreInterface::class, (new Target($name.'Store'))->getParsedName());
581595
}
582596
}
583597

@@ -608,6 +622,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
608622
->setArguments($arguments);
609623

610624
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
625+
$container->registerAliasForArgument('ai.store.'.$name, StoreInterface::class, (new Target($name.'Store'))->getParsedName());
611626
}
612627
}
613628

@@ -632,6 +647,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
632647
->setArguments($arguments);
633648

634649
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
650+
$container->registerAliasForArgument('ai.store.'.$name, StoreInterface::class, (new Target($name.'Store'))->getParsedName());
635651
}
636652
}
637653

@@ -663,6 +679,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
663679
->setArguments($arguments);
664680

665681
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
682+
$container->registerAliasForArgument('ai.store.'.$name, StoreInterface::class, (new Target($name.'Store'))->getParsedName());
666683
}
667684
}
668685

@@ -689,6 +706,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
689706
->setArguments($arguments);
690707

691708
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
709+
$container->registerAliasForArgument('ai.store.'.$name, StoreInterface::class, (new Target($name.'Store'))->getParsedName());
692710
}
693711
}
694712

@@ -726,6 +744,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
726744
->setArguments($arguments);
727745

728746
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
747+
$container->registerAliasForArgument('ai.store.'.$name, StoreInterface::class, (new Target($name.'Store'))->getParsedName());
729748
}
730749
}
731750

@@ -750,6 +769,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
750769
->setArguments($arguments);
751770

752771
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
772+
$container->registerAliasForArgument('ai.store.'.$name, StoreInterface::class, (new Target($name.'Store'))->getParsedName());
753773
}
754774
}
755775

@@ -776,6 +796,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
776796
->setArguments($arguments);
777797

778798
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
799+
$container->registerAliasForArgument('ai.store.'.$name, StoreInterface::class, (new Target($name.'Store'))->getParsedName());
779800
}
780801
}
781802

@@ -816,6 +837,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
816837
->setArguments($arguments);
817838

818839
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
840+
$container->registerAliasForArgument('ai.store.'.$name, StoreInterface::class, (new Target($name.'Store'))->getParsedName());
819841
}
820842
}
821843

@@ -842,6 +864,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
842864
->setArguments($arguments);
843865

844866
$container->setDefinition('ai.store.'.$type.'.'.$name, $definition);
867+
$container->registerAliasForArgument('ai.store.'.$name, StoreInterface::class, (new Target($name.'Store'))->getParsedName());
845868
}
846869
}
847870
}

src/ai-bundle/tests/DependencyInjection/AiBundleTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,23 @@ public function testExtensionLoadDoesNotThrow()
3434
$this->buildContainer($this->getFullConfig());
3535
}
3636

37+
public function testCommandsAreDefined()
38+
{
39+
$container = $this->buildContainer($this->getFullConfig());
40+
41+
$this->assertTrue($container->hasDefinition('console.command.ai.setup_store'));
42+
43+
$setupStoreCommandDefinition = $container->getDefinition('console.command.ai.setup_store');
44+
$this->assertCount(2, $setupStoreCommandDefinition->getArguments());
45+
$this->assertArrayHasKey('console.command', $setupStoreCommandDefinition->getTags());
46+
47+
$this->assertTrue($container->hasDefinition('console.command.ai.drop_store'));
48+
49+
$dropStoreCommandDefinition = $container->getDefinition('console.command.ai.drop_store');
50+
$this->assertCount(2, $dropStoreCommandDefinition->getArguments());
51+
$this->assertArrayHasKey('console.command', $dropStoreCommandDefinition->getTags());
52+
}
53+
3754
public function testInjectionAgentAliasIsRegistered()
3855
{
3956
$container = $this->buildContainer([

src/store/composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@
4141
"phpstan/phpstan": "^2.0",
4242
"phpunit/phpunit": "^11.5",
4343
"probots-io/pinecone-php": "^1.0",
44-
"symfony/cache": "^7.3"
44+
"symfony/cache": "^6.4 || ^7.1",
45+
"symfony/console": "^6.4 || ^7.1",
46+
"symfony/dependency-injection": "^6.4 || ^7.1"
4547
},
4648
"config": {
4749
"sort-packages": true
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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\Store\Command;
13+
14+
use Psr\Container\ContainerInterface;
15+
use Symfony\AI\Store\Exception\RuntimeException;
16+
use Symfony\AI\Store\ManagedStoreInterface;
17+
use Symfony\Component\Console\Attribute\AsCommand;
18+
use Symfony\Component\Console\Command\Command;
19+
use Symfony\Component\Console\Completion\CompletionInput;
20+
use Symfony\Component\Console\Completion\CompletionSuggestions;
21+
use Symfony\Component\Console\Input\InputArgument;
22+
use Symfony\Component\Console\Input\InputInterface;
23+
use Symfony\Component\Console\Output\OutputInterface;
24+
use Symfony\Component\Console\Style\SymfonyStyle;
25+
26+
/**
27+
* @author Guillaume Loulier <[email protected]>
28+
*/
29+
#[AsCommand(name: 'ai:store:drop', description: 'Drop the required infrastructure for the store')]
30+
final class DropStoreCommand extends Command
31+
{
32+
/**
33+
* @param string[] $storeNames
34+
*/
35+
public function __construct(
36+
private readonly ContainerInterface $storeLocator,
37+
private readonly array $storeNames = [],
38+
) {
39+
parent::__construct();
40+
}
41+
42+
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
43+
{
44+
if ($input->mustSuggestArgumentValuesFor('store')) {
45+
$suggestions->suggestValues($this->storeNames);
46+
}
47+
}
48+
49+
protected function configure(): void
50+
{
51+
$this
52+
->addArgument('store', InputArgument::OPTIONAL, 'Name of the store to drop')
53+
->setHelp(<<<EOF
54+
The <info>%command.name%</info> command drops the stores:
55+
56+
<info>php %command.full_name%</info>
57+
58+
Or a specific store only:
59+
60+
<info>php %command.full_name% <store></info>
61+
EOF
62+
)
63+
;
64+
}
65+
66+
protected function execute(InputInterface $input, OutputInterface $output): int
67+
{
68+
$io = new SymfonyStyle($input, $output);
69+
70+
$storeName = $input->getArgument('store');
71+
if (!$this->storeLocator->has($storeName)) {
72+
throw new RuntimeException(\sprintf('The "%s" store does not exist.', $storeName));
73+
}
74+
75+
$store = $this->storeLocator->get($storeName);
76+
if (!$store instanceof ManagedStoreInterface) {
77+
$io->note(\sprintf('The "%s" store does not support be dropped.', $storeName));
78+
79+
return Command::FAILURE;
80+
}
81+
82+
try {
83+
$store->drop();
84+
$io->success(\sprintf('The "%s" store was dropped successfully.', $storeName));
85+
} catch (\Exception $e) {
86+
throw new RuntimeException(\sprintf('An error occurred while dropping the "%s" store: ', $storeName).$e->getMessage(), 0, $e);
87+
}
88+
89+
return Command::SUCCESS;
90+
}
91+
}

0 commit comments

Comments
 (0)