diff --git a/src/ai-bundle/CHANGELOG.md b/src/ai-bundle/CHANGELOG.md index 620cdb5e1d..fad1b550ad 100644 --- a/src/ai-bundle/CHANGELOG.md +++ b/src/ai-bundle/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Add `chats` data from `DataCollector` to the `data_collector.html.twig` template * [BC BREAK] Rename service ID prefix `ai.toolbox.{agent}.agent_wrapper.` to `ai.toolbox.{agent}.subagent.` + * Add `ResetInterface` support to `TraceableChat`, `TraceableMessageStore`, `TraceablePlatform` and `TraceableToolbox` to clear collected data between requests 0.2 --- diff --git a/src/ai-bundle/composer.json b/src/ai-bundle/composer.json index 3988b23abf..fd33e11f5f 100644 --- a/src/ai-bundle/composer.json +++ b/src/ai-bundle/composer.json @@ -25,6 +25,7 @@ "symfony/console": "^7.3|^8.0", "symfony/dependency-injection": "^7.3|^8.0", "symfony/framework-bundle": "^7.3|^8.0", + "symfony/service-contracts": "^2.5|^3", "symfony/string": "^7.3|^8.0" }, "require-dev": { diff --git a/src/ai-bundle/src/AiBundle.php b/src/ai-bundle/src/AiBundle.php index 72d972712f..2d5cee1bac 100644 --- a/src/ai-bundle/src/AiBundle.php +++ b/src/ai-bundle/src/AiBundle.php @@ -182,7 +182,8 @@ public function loadExtension(array $config, ContainerConfigurator $container, C $traceablePlatformDefinition = (new Definition(TraceablePlatform::class)) ->setDecoratedService($platform, priority: -1024) ->setArguments([new Reference('.inner')]) - ->addTag('ai.traceable_platform'); + ->addTag('ai.traceable_platform') + ->addTag('kernel.reset', ['method' => 'reset']); $suffix = u($platform)->after('ai.platform.')->toString(); $builder->setDefinition('ai.traceable_platform.'.$suffix, $traceablePlatformDefinition); } @@ -259,7 +260,8 @@ public function loadExtension(array $config, ContainerConfigurator $container, C new Reference('.inner'), new Reference(ClockInterface::class), ]) - ->addTag('ai.traceable_message_store'); + ->addTag('ai.traceable_message_store') + ->addTag('kernel.reset', ['method' => 'reset']); $suffix = u($messageStore)->afterLast('.')->toString(); $builder->setDefinition('ai.traceable_message_store.'.$suffix, $traceableMessageStoreDefinition); } @@ -294,7 +296,8 @@ public function loadExtension(array $config, ContainerConfigurator $container, C new Reference('.inner'), new Reference(ClockInterface::class), ]) - ->addTag('ai.traceable_chat'); + ->addTag('ai.traceable_chat') + ->addTag('kernel.reset', ['method' => 'reset']); $suffix = u($chat)->afterLast('.')->toString(); $builder->setDefinition('ai.traceable_chat.'.$suffix, $traceableChatDefinition); } @@ -1121,7 +1124,8 @@ private function processAgentConfig(string $name, array $config, ContainerBuilde ->setClass(TraceableToolbox::class) ->setArguments([new Reference('.inner')]) ->setDecoratedService('ai.toolbox.'.$name, priority: -1024) - ->addTag('ai.traceable_toolbox'); + ->addTag('ai.traceable_toolbox') + ->addTag('kernel.reset', ['method' => 'reset']); $container->setDefinition('ai.traceable_toolbox.'.$name, $traceableToolboxDefinition); } @@ -1508,11 +1512,9 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde $definition = new Definition(InMemoryStore::class); $definition - ->setLazy(true) ->setArguments($arguments) - ->addTag('proxy', ['interface' => StoreInterface::class]) - ->addTag('proxy', ['interface' => ManagedStoreInterface::class]) - ->addTag('ai.store'); + ->addTag('ai.store') + ->addTag('kernel.reset', ['method' => 'reset']); $container->setDefinition('ai.store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.store.'.$type.'.'.$name, StoreInterface::class, $name); @@ -2060,11 +2062,9 @@ private function processMessageStoreConfig(string $type, array $messageStores, C foreach ($messageStores as $name => $messageStore) { $definition = new Definition(InMemoryMessageStore::class); $definition - ->setLazy(true) ->setArgument(0, $messageStore['identifier']) - ->addTag('proxy', ['interface' => MessageStoreInterface::class]) - ->addTag('proxy', ['interface' => ManagedMessageStoreInterface::class]) - ->addTag('ai.message_store'); + ->addTag('ai.message_store') + ->addTag('kernel.reset', ['method' => 'reset']); $container->setDefinition('ai.message_store.'.$type.'.'.$name, $definition); $container->registerAliasForArgument('ai.message_store.'.$type.'.'.$name, MessageStoreInterface::class, $name); diff --git a/src/ai-bundle/src/Profiler/TraceableChat.php b/src/ai-bundle/src/Profiler/TraceableChat.php index f6d834e431..df838b3136 100644 --- a/src/ai-bundle/src/Profiler/TraceableChat.php +++ b/src/ai-bundle/src/Profiler/TraceableChat.php @@ -16,6 +16,7 @@ use Symfony\AI\Platform\Message\MessageBag; use Symfony\AI\Platform\Message\UserMessage; use Symfony\Component\Clock\ClockInterface; +use Symfony\Contracts\Service\ResetInterface; /** * @author Guillaume Loulier @@ -27,7 +28,7 @@ * saved_at: \DateTimeImmutable, * } */ -final class TraceableChat implements ChatInterface +final class TraceableChat implements ChatInterface, ResetInterface { /** * @var arraychat->submit($message); } + + public function reset(): void + { + if ($this->chat instanceof ResetInterface) { + $this->chat->reset(); + } + $this->calls = []; + } } diff --git a/src/ai-bundle/src/Profiler/TraceableMessageStore.php b/src/ai-bundle/src/Profiler/TraceableMessageStore.php index 34b426e3e3..077f7ef6e0 100644 --- a/src/ai-bundle/src/Profiler/TraceableMessageStore.php +++ b/src/ai-bundle/src/Profiler/TraceableMessageStore.php @@ -15,6 +15,7 @@ use Symfony\AI\Chat\MessageStoreInterface; use Symfony\AI\Platform\Message\MessageBag; use Symfony\Component\Clock\ClockInterface; +use Symfony\Contracts\Service\ResetInterface; /** * @author Guillaume Loulier @@ -24,7 +25,7 @@ * saved_at: \DateTimeImmutable, * } */ -final class TraceableMessageStore implements ManagedStoreInterface, MessageStoreInterface +final class TraceableMessageStore implements ManagedStoreInterface, MessageStoreInterface, ResetInterface { /** * @var MessageStoreData[] @@ -69,4 +70,12 @@ public function drop(): void $this->messageStore->drop(); } + + public function reset(): void + { + if ($this->messageStore instanceof ResetInterface) { + $this->messageStore->reset(); + } + $this->calls = []; + } } diff --git a/src/ai-bundle/src/Profiler/TraceablePlatform.php b/src/ai-bundle/src/Profiler/TraceablePlatform.php index 1c0d9e1d7c..f2aee81763 100644 --- a/src/ai-bundle/src/Profiler/TraceablePlatform.php +++ b/src/ai-bundle/src/Profiler/TraceablePlatform.php @@ -19,6 +19,7 @@ use Symfony\AI\Platform\Result\DeferredResult; use Symfony\AI\Platform\Result\ResultInterface; use Symfony\AI\Platform\Result\StreamResult; +use Symfony\Contracts\Service\ResetInterface; /** * @author Christopher Hertel @@ -30,7 +31,7 @@ * result: DeferredResult, * } */ -final class TraceablePlatform implements PlatformInterface +final class TraceablePlatform implements PlatformInterface, ResetInterface { /** * @var PlatformCallData[] @@ -74,6 +75,12 @@ public function getModelCatalog(): ModelCatalogInterface return $this->platform->getModelCatalog(); } + public function reset(): void + { + $this->calls = []; + $this->resultCache = new \WeakMap(); + } + private function createTraceableStreamResult(DeferredResult $originalStream): StreamResult { return $result = new StreamResult((function () use (&$result, $originalStream) { diff --git a/src/ai-bundle/src/Profiler/TraceableToolbox.php b/src/ai-bundle/src/Profiler/TraceableToolbox.php index d1dd10481d..d5d069b638 100644 --- a/src/ai-bundle/src/Profiler/TraceableToolbox.php +++ b/src/ai-bundle/src/Profiler/TraceableToolbox.php @@ -14,11 +14,12 @@ use Symfony\AI\Agent\Toolbox\ToolboxInterface; use Symfony\AI\Agent\Toolbox\ToolResult; use Symfony\AI\Platform\Result\ToolCall; +use Symfony\Contracts\Service\ResetInterface; /** * @author Christopher Hertel */ -final class TraceableToolbox implements ToolboxInterface +final class TraceableToolbox implements ToolboxInterface, ResetInterface { /** * @var ToolResult[] @@ -39,4 +40,9 @@ public function execute(ToolCall $toolCall): ToolResult { return $this->calls[] = $this->toolbox->execute($toolCall); } + + public function reset(): void + { + $this->calls = []; + } } diff --git a/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php b/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php index da5cf9fdc0..c02c6e582f 100644 --- a/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php +++ b/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php @@ -1530,17 +1530,14 @@ public function testInMemoryStoreWithoutCustomStrategyCanBeConfigured() $definition = $container->getDefinition('ai.store.memory.my_memory_store_with_custom_strategy'); $this->assertSame(InMemoryStore::class, $definition->getClass()); - $this->assertTrue($definition->isLazy()); + $this->assertFalse($definition->isLazy()); $this->assertCount(1, $definition->getArguments()); $this->assertInstanceOf(Definition::class, $definition->getArgument(0)); $this->assertSame(DistanceCalculator::class, $definition->getArgument(0)->getClass()); - $this->assertTrue($definition->hasTag('proxy')); - $this->assertSame([ - ['interface' => StoreInterface::class], - ['interface' => ManagedStoreInterface::class], - ], $definition->getTag('proxy')); + $this->assertFalse($definition->hasTag('proxy')); $this->assertTrue($definition->hasTag('ai.store')); + $this->assertTrue($definition->hasTag('kernel.reset')); $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_memory_store_with_custom_strategy')); $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myMemoryStoreWithCustomStrategy')); @@ -1569,17 +1566,14 @@ public function testInMemoryStoreWithCustomStrategyCanBeConfigured() $definition = $container->getDefinition('ai.store.memory.my_memory_store_with_custom_strategy'); $this->assertSame(InMemoryStore::class, $definition->getClass()); - $this->assertTrue($definition->isLazy()); + $this->assertFalse($definition->isLazy()); $this->assertCount(1, $definition->getArguments()); $this->assertInstanceOf(Reference::class, $definition->getArgument(0)); $this->assertSame('ai.store.distance_calculator.my_memory_store_with_custom_strategy', (string) $definition->getArgument(0)); - $this->assertTrue($definition->hasTag('proxy')); - $this->assertSame([ - ['interface' => StoreInterface::class], - ['interface' => ManagedStoreInterface::class], - ], $definition->getTag('proxy')); + $this->assertFalse($definition->hasTag('proxy')); $this->assertTrue($definition->hasTag('ai.store')); + $this->assertTrue($definition->hasTag('kernel.reset')); $this->assertTrue($container->hasAlias('.Symfony\AI\Store\StoreInterface $my_memory_store_with_custom_strategy')); $this->assertTrue($container->hasAlias('Symfony\AI\Store\StoreInterface $myMemoryStoreWithCustomStrategy')); @@ -6709,14 +6703,12 @@ public function testMemoryMessageStoreCanBeConfiguredWithCustomKey() $definition = $container->getDefinition('ai.message_store.memory.custom'); - $this->assertTrue($definition->isLazy()); + $this->assertFalse($definition->isLazy()); $this->assertSame('foo', $definition->getArgument(0)); - $this->assertSame([ - ['interface' => MessageStoreInterface::class], - ['interface' => ManagedMessageStoreInterface::class], - ], $definition->getTag('proxy')); + $this->assertFalse($definition->hasTag('proxy')); $this->assertTrue($definition->hasTag('ai.message_store')); + $this->assertTrue($definition->hasTag('kernel.reset')); } public function testMongoDbMessageStoreIsConfigured() diff --git a/src/ai-bundle/tests/Profiler/TraceableChatTest.php b/src/ai-bundle/tests/Profiler/TraceableChatTest.php index e954483835..c7e7aec629 100644 --- a/src/ai-bundle/tests/Profiler/TraceableChatTest.php +++ b/src/ai-bundle/tests/Profiler/TraceableChatTest.php @@ -60,4 +60,17 @@ public function testInitializationMessageBagCanBeRetrieved() $this->assertInstanceOf(UserMessage::class, $traceableChat->calls[1]['message']); $this->assertInstanceOf(\DateTimeImmutable::class, $traceableChat->calls[1]['saved_at']); } + + public function testResetClearsCalls() + { + $agent = $this->createStub(AgentInterface::class); + $chat = new Chat($agent, new InMemoryStore()); + $traceableChat = new TraceableChat($chat, new MonotonicClock()); + + $traceableChat->initiate(new MessageBag()); + $this->assertCount(1, $traceableChat->calls); + + $traceableChat->reset(); + $this->assertCount(0, $traceableChat->calls); + } } diff --git a/src/ai-bundle/tests/Profiler/TraceableMessageStoreTest.php b/src/ai-bundle/tests/Profiler/TraceableMessageStoreTest.php index bc9aac2279..9971415728 100644 --- a/src/ai-bundle/tests/Profiler/TraceableMessageStoreTest.php +++ b/src/ai-bundle/tests/Profiler/TraceableMessageStoreTest.php @@ -42,4 +42,16 @@ public function testSubmittedMessageBagCanBeRetrieved() $this->assertCount(1, $calls[0]['bag']); $this->assertInstanceOf(\DateTimeImmutable::class, $calls[0]['saved_at']); } + + public function testResetClearsCalls() + { + $messageStore = new InMemoryStore(); + $traceableMessageStore = new TraceableMessageStore($messageStore, new MonotonicClock()); + + $traceableMessageStore->save(new MessageBag()); + $this->assertCount(1, $traceableMessageStore->calls); + + $traceableMessageStore->reset(); + $this->assertCount(0, $traceableMessageStore->calls); + } } diff --git a/src/ai-bundle/tests/Profiler/TraceablePlatformTest.php b/src/ai-bundle/tests/Profiler/TraceablePlatformTest.php new file mode 100644 index 0000000000..7be27c2bd6 --- /dev/null +++ b/src/ai-bundle/tests/Profiler/TraceablePlatformTest.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\AI\AiBundle\Tests\Profiler; + +use PHPUnit\Framework\TestCase; +use Symfony\AI\AiBundle\Profiler\TraceablePlatform; +use Symfony\AI\Platform\PlainConverter; +use Symfony\AI\Platform\PlatformInterface; +use Symfony\AI\Platform\Result\DeferredResult; +use Symfony\AI\Platform\Result\RawResultInterface; +use Symfony\AI\Platform\Result\TextResult; + +final class TraceablePlatformTest extends TestCase +{ + public function testResetClearsCallsAndResultCache() + { + $platform = $this->createStub(PlatformInterface::class); + $traceablePlatform = new TraceablePlatform($platform); + $result = new TextResult('Assistant response'); + + $platform->method('invoke')->willReturn(new DeferredResult(new PlainConverter($result), $this->createStub(RawResultInterface::class))); + + $traceablePlatform->invoke('gpt-4o', 'Hello'); + $this->assertCount(1, $traceablePlatform->calls); + $this->assertSame('gpt-4o', $traceablePlatform->calls[0]['model']); + $this->assertSame('Hello', $traceablePlatform->calls[0]['input']); + + $oldCache = $traceablePlatform->resultCache; + + $traceablePlatform->reset(); + + $this->assertCount(0, $traceablePlatform->calls); + $this->assertNotSame($oldCache, $traceablePlatform->resultCache); + $this->assertInstanceOf(\WeakMap::class, $traceablePlatform->resultCache); + } +} diff --git a/src/ai-bundle/tests/Profiler/TraceableToolboxTest.php b/src/ai-bundle/tests/Profiler/TraceableToolboxTest.php index f244bee52f..8f67d9b41c 100644 --- a/src/ai-bundle/tests/Profiler/TraceableToolboxTest.php +++ b/src/ai-bundle/tests/Profiler/TraceableToolboxTest.php @@ -47,6 +47,18 @@ public function testExecute() $this->assertSame('tool_result', $traceableToolbox->calls[0]->getResult()); } + public function testResetClearsCalls() + { + $toolbox = $this->createToolbox([]); + $traceableToolbox = new TraceableToolbox($toolbox); + + $traceableToolbox->execute(new ToolCall('foo', '__invoke')); + $this->assertCount(1, $traceableToolbox->calls); + + $traceableToolbox->reset(); + $this->assertCount(0, $traceableToolbox->calls); + } + /** * @param Tool[] $tools */ diff --git a/src/chat/CHANGELOG.md b/src/chat/CHANGELOG.md index 86009a85af..bcdaf254c4 100644 --- a/src/chat/CHANGELOG.md +++ b/src/chat/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +0.4 +--- + + * Add `ResetInterface` support to in-memory store + 0.1 --- diff --git a/src/chat/composer.json b/src/chat/composer.json index 9f1ef52c7e..55046f2ecf 100644 --- a/src/chat/composer.json +++ b/src/chat/composer.json @@ -26,7 +26,8 @@ "require": { "php": ">=8.2", "symfony/ai-agent": "^0.3", - "symfony/ai-platform": "^0.3" + "symfony/ai-platform": "^0.3", + "symfony/service-contracts": "^2.5|^3" }, "require-dev": { "phpstan/phpstan": "^2.1", diff --git a/src/chat/src/InMemory/Store.php b/src/chat/src/InMemory/Store.php index 08f3267343..3db6ecd0f3 100644 --- a/src/chat/src/InMemory/Store.php +++ b/src/chat/src/InMemory/Store.php @@ -14,11 +14,12 @@ use Symfony\AI\Chat\ManagedStoreInterface; use Symfony\AI\Chat\MessageStoreInterface; use Symfony\AI\Platform\Message\MessageBag; +use Symfony\Contracts\Service\ResetInterface; /** * @author Christopher Hertel */ -final class Store implements ManagedStoreInterface, MessageStoreInterface +final class Store implements ManagedStoreInterface, MessageStoreInterface, ResetInterface { /** * @var MessageBag[] @@ -49,4 +50,9 @@ public function drop(): void { $this->messages[$this->identifier] = new MessageBag(); } + + public function reset(): void + { + $this->messages = []; + } } diff --git a/src/chat/tests/InMemory/StoreTest.php b/src/chat/tests/InMemory/StoreTest.php index 17afd8cfd1..dce2e3b337 100644 --- a/src/chat/tests/InMemory/StoreTest.php +++ b/src/chat/tests/InMemory/StoreTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\AI\Chat\InMemory\Store; +use Symfony\AI\Platform\Message\AssistantMessage; use Symfony\AI\Platform\Message\Content\Text; use Symfony\AI\Platform\Message\Message; use Symfony\AI\Platform\Message\MessageBag; @@ -102,4 +103,18 @@ public function testSetupOptions() $messages = $store->load(); $this->assertInstanceOf(MessageBag::class, $messages); } + + public function testResetClearsMessages() + { + $store = new Store(); + $store->setup(); + $store->save(new MessageBag(new UserMessage(new Text('Hello')))); + $store->save(new MessageBag(new UserMessage(new Text('Hello')), new AssistantMessage('Hi'))); + + $this->assertCount(2, $store->load()); + + $store->reset(); + + $this->assertCount(0, $store->load()); + } } diff --git a/src/mcp-bundle/CHANGELOG.md b/src/mcp-bundle/CHANGELOG.md index 920fcb1b83..08047eb6b3 100644 --- a/src/mcp-bundle/CHANGELOG.md +++ b/src/mcp-bundle/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +0.4 +--- + + * Add `ResetInterface` support to `TraceableRegistry` to clear collected data between requests + 0.3 --- diff --git a/src/mcp-bundle/composer.json b/src/mcp-bundle/composer.json index b388b41267..f55a026f46 100644 --- a/src/mcp-bundle/composer.json +++ b/src/mcp-bundle/composer.json @@ -23,7 +23,8 @@ "symfony/http-foundation": "^7.3|^8.0", "symfony/http-kernel": "^7.3|^8.0", "symfony/psr-http-message-bridge": "^7.3|^8.0", - "symfony/routing": "^7.3|^8.0" + "symfony/routing": "^7.3|^8.0", + "symfony/service-contracts": "^2.5|^3" }, "require-dev": { "phpstan/phpstan": "^2.1", diff --git a/src/mcp-bundle/src/McpBundle.php b/src/mcp-bundle/src/McpBundle.php index 34ccd06ca0..77015c71a4 100644 --- a/src/mcp-bundle/src/McpBundle.php +++ b/src/mcp-bundle/src/McpBundle.php @@ -76,7 +76,8 @@ public function loadExtension(array $config, ContainerConfigurator $container, C $traceableRegistry = (new Definition('mcp.traceable_registry')) ->setClass(TraceableRegistry::class) ->setArguments([new Reference('.inner')]) - ->setDecoratedService('mcp.registry'); + ->setDecoratedService('mcp.registry') + ->addTag('kernel.reset', ['method' => 'reset']); $builder->setDefinition('mcp.traceable_registry', $traceableRegistry); $dataCollector = (new Definition(DataCollector::class)) diff --git a/src/mcp-bundle/src/Profiler/TraceableRegistry.php b/src/mcp-bundle/src/Profiler/TraceableRegistry.php index 369d4fd4aa..2ccdbdc707 100644 --- a/src/mcp-bundle/src/Profiler/TraceableRegistry.php +++ b/src/mcp-bundle/src/Profiler/TraceableRegistry.php @@ -22,13 +22,14 @@ use Mcp\Schema\Resource; use Mcp\Schema\ResourceTemplate; use Mcp\Schema\Tool; +use Symfony\Contracts\Service\ResetInterface; /** * Decorator for Registry that provides access to capabilities for the profiler. * * @author Camille Islasse */ -final class TraceableRegistry implements RegistryInterface +final class TraceableRegistry implements RegistryInterface, ResetInterface { public function __construct( private readonly RegistryInterface $registry, @@ -137,4 +138,9 @@ public function getPrompt(string $name): PromptReference { return $this->registry->getPrompt($name); } + + public function reset(): void + { + $this->clear(); + } } diff --git a/src/mcp-bundle/tests/Profiler/TraceableRegistryTest.php b/src/mcp-bundle/tests/Profiler/TraceableRegistryTest.php new file mode 100644 index 0000000000..6e8a508a11 --- /dev/null +++ b/src/mcp-bundle/tests/Profiler/TraceableRegistryTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\AI\McpBundle\Tests\Profiler; + +use Mcp\Capability\RegistryInterface; +use PHPUnit\Framework\TestCase; +use Symfony\AI\McpBundle\Profiler\TraceableRegistry; + +final class TraceableRegistryTest extends TestCase +{ + public function testResetCallsClear() + { + $registry = $this->createMock(RegistryInterface::class); + $registry->expects($this->once())->method('clear'); + + $traceableRegistry = new TraceableRegistry($registry); + $traceableRegistry->reset(); + } +} diff --git a/src/store/CHANGELOG.md b/src/store/CHANGELOG.md index c2cf542200..aa7c4b4f8f 100644 --- a/src/store/CHANGELOG.md +++ b/src/store/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG * Add `ConfiguredSourceIndexer` decorator for pre-configuring default sources on `SourceIndexer` * [BC BREAK] Remove `Indexer` class - use `SourceIndexer` or `DocumentIndexer` instead * [BC BREAK] Change `IndexerInterface::index()` signature - input parameter is no longer nullable + * Add `ResetInterface` support to in-memory store 0.3 --- diff --git a/src/store/composer.json b/src/store/composer.json index 438b1ee957..dfaabc671a 100644 --- a/src/store/composer.json +++ b/src/store/composer.json @@ -46,6 +46,7 @@ "symfony/clock": "^7.3|^8.0", "symfony/http-client": "^7.3|^8.0", "symfony/polyfill-php83": "^1.32", + "symfony/service-contracts": "^2.5|^3", "symfony/uid": "^7.3|^8.0" }, "require-dev": { diff --git a/src/store/src/InMemory/Store.php b/src/store/src/InMemory/Store.php index 6e2dfb4789..7a41fe9046 100644 --- a/src/store/src/InMemory/Store.php +++ b/src/store/src/InMemory/Store.php @@ -17,11 +17,12 @@ use Symfony\AI\Store\Exception\InvalidArgumentException; use Symfony\AI\Store\ManagedStoreInterface; use Symfony\AI\Store\StoreInterface; +use Symfony\Contracts\Service\ResetInterface; /** * @author Guillaume Loulier */ -class Store implements ManagedStoreInterface, StoreInterface +class Store implements ManagedStoreInterface, StoreInterface, ResetInterface { /** * @var VectorDocument[] @@ -88,4 +89,9 @@ public function drop(array $options = []): void { $this->documents = []; } + + public function reset(): void + { + $this->drop(); + } } diff --git a/src/store/tests/InMemory/StoreTest.php b/src/store/tests/InMemory/StoreTest.php index 49f5518351..21fa10a6ca 100644 --- a/src/store/tests/InMemory/StoreTest.php +++ b/src/store/tests/InMemory/StoreTest.php @@ -366,4 +366,17 @@ public function testStoreRemoveThrowsExceptionWhenOptionsProvided() $store->remove((string) $id, ['unsupported' => 'option']); } + + public function testResetClearsDocuments() + { + $store = new Store(); + $store->add(new VectorDocument('1', new Vector([1.0, 2.0]))); + $store->add(new VectorDocument('2', new Vector([3.0, 4.0]))); + + $this->assertCount(2, iterator_to_array($store->query(new Vector([1.0, 2.0])))); + + $store->reset(); + + $this->assertCount(0, iterator_to_array($store->query(new Vector([1.0, 2.0])))); + } }