Skip to content

[Platform] Add InMemoryPlatform and InMemoryRawResult for testing #193

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/platform/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,6 @@ CHANGELOG
* Add exception handling with specific error types
* Add support for embeddings generation across multiple providers
* Add response promises for async operations
* Add InMemoryPlatform and InMemoryRawResult for testing Platform without external Providers calls


24 changes: 24 additions & 0 deletions src/platform/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,30 @@ which can be useful to speed up the processing::
echo $result->asText().PHP_EOL;
}

Testing Tools
-------------

For unit or integration testing, you can use the `InMemoryPlatform`, which implements `PlatformInterface` without calling external APIs.

It supports returning either:

- A fixed string result
- A callable that dynamically returns a response based on the model, input, and options::

use Symfony\AI\Platform\InMemoryPlatform;
use Symfony\AI\Platform\Model;

$platform = new InMemoryPlatform('Fake result');

$result = $platform->invoke(new Model('test'), 'What is the capital of France?');

echo $result->asText(); // "Fake result"


Internally, it uses `InMemoryRawResult` to simulate the behavior of real API responses and support `ResultPromise`.

This allows fast and isolated testing of AI-powered features without relying on live providers or HTTP requests.

.. note::

This requires `cURL` and the `ext-curl` extension to be installed.
Expand Down
52 changes: 52 additions & 0 deletions src/platform/src/InMemoryPlatform.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\AI\Platform;

use Symfony\AI\Platform\Result\InMemoryRawResult;
use Symfony\AI\Platform\Result\ResultPromise;
use Symfony\AI\Platform\Result\TextResult;

/**
* A fake implementation of PlatformInterface that returns fixed or callable responses.
*
* Useful for unit or integration testing without real API calls.
*
* @author Ramy Hakam <[email protected]>
*/
class InMemoryPlatform implements PlatformInterface
{
/**
* The mock result can be a string or a callable that returns a string.
* If it's a closure, it receives the model, input, and optionally options as parameters like a real platform call.
*/
public function __construct(private readonly \Closure|string $mockResult)
{
}

public function invoke(Model $model, array|string|object $input, array $options = []): ResultPromise
{
$resultText = $this->mockResult instanceof \Closure
? ($this->mockResult)($model, $input, $options)
: $this->mockResult;

$textResult = new TextResult($resultText);

return new ResultPromise(
static fn () => $textResult,
rawResult: new InMemoryRawResult(
['text' => $resultText],
(object) ['text' => $resultText],
),
options: $options
);
}
}
39 changes: 39 additions & 0 deletions src/platform/src/Result/InMemoryRawResult.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\AI\Platform\Result;

/**
* A fake implementation of RawResultInterface that returns fixed data.
*
* @author Ramy Hakam <[email protected]>
*/
final readonly class InMemoryRawResult implements RawResultInterface
{
/**
* @param array<string, mixed> $data
*/
public function __construct(
private array $data = [],
private object $object = new \stdClass(),
) {
}

public function getData(): array
{
return $this->data;
}

public function getObject(): object
{
return $this->object;
}
}
43 changes: 43 additions & 0 deletions src/platform/tests/InMemoryPlatformTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Symfony\AI\Platform\InMemoryPlatform;
use Symfony\AI\Platform\Model;

#[CoversClass(InMemoryPlatform::class)]
class InMemoryPlatformTest extends TestCase
{
#[Test]
public function platformInvokeWithFixedResponse(): void
{
$platform = new InMemoryPlatform('Mocked result');
$result = $platform->invoke(new Model('test'), 'input');

$this->assertSame('Mocked result', $result->asText());
$this->assertSame('Mocked result', $result->getResult()->getContent());
$this->assertSame(['text' => 'Mocked result'], $result->getRawResult()->getData());
}

#[Test]
public function platformInvokeWithCallableResponse(): void
{
$platform = new InMemoryPlatform(function (Model $model, $input) {
return strtoupper((string) $input);
});

$result = $platform->invoke(new Model('test'), 'dynamic text');

$this->assertSame('DYNAMIC TEXT', $result->asText());
}
}