mirror of
https://github.com/symfony/ai.git
synced 2026-03-23 23:42:18 +01:00
Decouple LM Studio bridge from Open AI in favor of Generic bridge & introduce concrete ModelCatalog to fix examples
This commit is contained in:
24
examples/lmstudio/embeddings.php
Normal file
24
examples/lmstudio/embeddings.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Symfony\AI\Platform\Bridge\LmStudio\PlatformFactory;
|
||||
|
||||
require_once dirname(__DIR__).'/bootstrap.php';
|
||||
|
||||
$platform = PlatformFactory::create(env('LMSTUDIO_HOST_URL'), http_client());
|
||||
|
||||
$result = $platform->invoke('text-embedding-nomic-embed-text-v2-moe', <<<TEXT
|
||||
Once upon a time, there was a country called Japan. It was a beautiful country with a lot of mountains and rivers.
|
||||
The people of Japan were very kind and hardworking. They loved their country very much and took care of it. The
|
||||
country was very peaceful and prosperous. The people lived happily ever after.
|
||||
TEXT);
|
||||
|
||||
print_vectors($result);
|
||||
@@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\AI\Platform\Bridge\LmStudio;
|
||||
|
||||
use Symfony\AI\Platform\Model;
|
||||
|
||||
/**
|
||||
* @author André Lubian <lubiana123@gmail.com>
|
||||
*/
|
||||
class Completions extends Model
|
||||
{
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\AI\Platform\Bridge\LmStudio\Completions;
|
||||
|
||||
use Symfony\AI\Platform\Bridge\LmStudio\Completions;
|
||||
use Symfony\AI\Platform\Model;
|
||||
use Symfony\AI\Platform\ModelClientInterface;
|
||||
use Symfony\AI\Platform\Result\RawHttpResult;
|
||||
use Symfony\Component\HttpClient\EventSourceHttpClient;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
/**
|
||||
* @author André Lubian <lubiana123@gmail.com>
|
||||
*/
|
||||
final class ModelClient implements ModelClientInterface
|
||||
{
|
||||
private readonly EventSourceHttpClient $httpClient;
|
||||
|
||||
public function __construct(
|
||||
HttpClientInterface $httpClient,
|
||||
private readonly string $hostUrl,
|
||||
) {
|
||||
$this->httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);
|
||||
}
|
||||
|
||||
public function supports(Model $model): bool
|
||||
{
|
||||
return $model instanceof Completions;
|
||||
}
|
||||
|
||||
public function request(Model $model, array|string $payload, array $options = []): RawHttpResult
|
||||
{
|
||||
return new RawHttpResult($this->httpClient->request('POST', \sprintf('%s/v1/chat/completions', $this->hostUrl), [
|
||||
'json' => array_merge($options, $payload),
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\AI\Platform\Bridge\LmStudio\Completions;
|
||||
|
||||
use Symfony\AI\Platform\Bridge\LmStudio\Completions;
|
||||
use Symfony\AI\Platform\Bridge\OpenAi\Gpt\ResultConverter as OpenAiResponseConverter;
|
||||
use Symfony\AI\Platform\Model;
|
||||
use Symfony\AI\Platform\Result\RawResultInterface;
|
||||
use Symfony\AI\Platform\Result\ResultInterface;
|
||||
use Symfony\AI\Platform\ResultConverterInterface;
|
||||
|
||||
/**
|
||||
* @author André Lubian <lubiana123@gmail.com>
|
||||
*/
|
||||
final class ResultConverter implements ResultConverterInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly OpenAiResponseConverter $gptResponseConverter = new OpenAiResponseConverter(),
|
||||
) {
|
||||
}
|
||||
|
||||
public function supports(Model $model): bool
|
||||
{
|
||||
return $model instanceof Completions;
|
||||
}
|
||||
|
||||
public function convert(RawResultInterface $result, array $options = []): ResultInterface
|
||||
{
|
||||
return $this->gptResponseConverter->convert($result, $options);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\AI\Platform\Bridge\LmStudio;
|
||||
|
||||
use Symfony\AI\Platform\Model;
|
||||
|
||||
/**
|
||||
* @author André Lubian <lubiana123@gmail.com>
|
||||
*/
|
||||
class Embeddings extends Model
|
||||
{
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\AI\Platform\Bridge\LmStudio\Embeddings;
|
||||
|
||||
use Symfony\AI\Platform\Bridge\LmStudio\Embeddings;
|
||||
use Symfony\AI\Platform\Model;
|
||||
use Symfony\AI\Platform\ModelClientInterface;
|
||||
use Symfony\AI\Platform\Result\RawHttpResult;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
/**
|
||||
* @author Christopher Hertel <mail@christopher-hertel.de>
|
||||
* @author André Lubian <lubiana123@gmail.com>
|
||||
*/
|
||||
final class ModelClient implements ModelClientInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly HttpClientInterface $httpClient,
|
||||
private readonly string $hostUrl,
|
||||
) {
|
||||
}
|
||||
|
||||
public function supports(Model $model): bool
|
||||
{
|
||||
return $model instanceof Embeddings;
|
||||
}
|
||||
|
||||
public function request(Model $model, array|string $payload, array $options = []): RawHttpResult
|
||||
{
|
||||
return new RawHttpResult($this->httpClient->request('POST', \sprintf('%s/v1/embeddings', $this->hostUrl), [
|
||||
'json' => array_merge($options, [
|
||||
'model' => $model->getName(),
|
||||
'input' => $payload,
|
||||
]),
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\AI\Platform\Bridge\LmStudio\Embeddings;
|
||||
|
||||
use Symfony\AI\Platform\Bridge\LmStudio\Embeddings;
|
||||
use Symfony\AI\Platform\Exception\RuntimeException;
|
||||
use Symfony\AI\Platform\Model;
|
||||
use Symfony\AI\Platform\Result\RawResultInterface;
|
||||
use Symfony\AI\Platform\Result\VectorResult;
|
||||
use Symfony\AI\Platform\ResultConverterInterface;
|
||||
use Symfony\AI\Platform\Vector\Vector;
|
||||
|
||||
/**
|
||||
* @author Christopher Hertel <mail@christopher-hertel.de>
|
||||
* @author André Lubian <lubiana123@gmail.com>
|
||||
*/
|
||||
final class ResultConverter implements ResultConverterInterface
|
||||
{
|
||||
public function supports(Model $model): bool
|
||||
{
|
||||
return $model instanceof Embeddings;
|
||||
}
|
||||
|
||||
public function convert(RawResultInterface $result, array $options = []): VectorResult
|
||||
{
|
||||
$data = $result->getData();
|
||||
|
||||
if (!isset($data['data'])) {
|
||||
throw new RuntimeException('Response does not contain data.');
|
||||
}
|
||||
|
||||
return new VectorResult(
|
||||
...array_map(
|
||||
static fn (array $item): Vector => new Vector($item['embedding']),
|
||||
$data['data']
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,13 +11,41 @@
|
||||
|
||||
namespace Symfony\AI\Platform\Bridge\LmStudio;
|
||||
|
||||
use Symfony\AI\Platform\ModelCatalog\FallbackModelCatalog;
|
||||
use Symfony\AI\Platform\Bridge\Generic\CompletionsModel;
|
||||
use Symfony\AI\Platform\Bridge\Generic\EmbeddingsModel;
|
||||
use Symfony\AI\Platform\Capability;
|
||||
use Symfony\AI\Platform\ModelCatalog\AbstractModelCatalog;
|
||||
|
||||
/**
|
||||
* @author Oskar Stark <oskarstark@googlemail.com>
|
||||
*/
|
||||
final class ModelCatalog extends FallbackModelCatalog
|
||||
final class ModelCatalog extends AbstractModelCatalog
|
||||
{
|
||||
// LmStudio can use any model that is loaded locally
|
||||
// Models are dynamically available based on what's loaded in LmStudio
|
||||
/**
|
||||
* @param array<string, array{class: string, capabilities: list<Capability>}> $additionalModels
|
||||
*/
|
||||
public function __construct(array $additionalModels = [])
|
||||
{
|
||||
$defaultModels = [
|
||||
'gemma-3-4b-it-qat' => [
|
||||
'class' => CompletionsModel::class,
|
||||
'capabilities' => [
|
||||
Capability::INPUT_MESSAGES,
|
||||
Capability::INPUT_IMAGE,
|
||||
Capability::OUTPUT_TEXT,
|
||||
Capability::OUTPUT_STREAMING,
|
||||
Capability::TOOL_CALLING,
|
||||
],
|
||||
],
|
||||
'text-embedding-nomic-embed-text-v2-moe' => [
|
||||
'class' => EmbeddingsModel::class,
|
||||
'capabilities' => [
|
||||
Capability::INPUT_TEXT,
|
||||
Capability::EMBEDDINGS,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->models = array_merge($defaultModels, $additionalModels);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
namespace Symfony\AI\Platform\Bridge\LmStudio;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\AI\Platform\Bridge\LmStudio\Embeddings\ModelClient;
|
||||
use Symfony\AI\Platform\Bridge\Generic\Completions;
|
||||
use Symfony\AI\Platform\Bridge\Generic\Embeddings;
|
||||
use Symfony\AI\Platform\Contract;
|
||||
use Symfony\AI\Platform\ModelCatalog\ModelCatalogInterface;
|
||||
use Symfony\AI\Platform\Platform;
|
||||
@@ -25,7 +26,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
class PlatformFactory
|
||||
{
|
||||
public static function create(
|
||||
string $hostUrl = 'http://localhost:1234',
|
||||
string $baseUrl = 'http://localhost:1234',
|
||||
?HttpClientInterface $httpClient = null,
|
||||
ModelCatalogInterface $modelCatalog = new ModelCatalog(),
|
||||
?Contract $contract = null,
|
||||
@@ -35,15 +36,15 @@ class PlatformFactory
|
||||
|
||||
return new Platform(
|
||||
[
|
||||
new ModelClient($httpClient, $hostUrl),
|
||||
new Completions\ModelClient($httpClient, $hostUrl),
|
||||
new Completions\ModelClient($httpClient, $baseUrl),
|
||||
new Embeddings\ModelClient($httpClient, $baseUrl),
|
||||
],
|
||||
[
|
||||
new Embeddings\ResultConverter(),
|
||||
new Completions\ResultConverter(),
|
||||
],
|
||||
$modelCatalog,
|
||||
$contract,
|
||||
$contract ?? Contract::create(),
|
||||
$eventDispatcher,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\AI\Platform\Tests\Bridge\LmStudio\Completions;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\AI\Platform\Bridge\LmStudio\Completions;
|
||||
use Symfony\AI\Platform\Bridge\LmStudio\Completions\ModelClient;
|
||||
use Symfony\Component\HttpClient\EventSourceHttpClient;
|
||||
use Symfony\Component\HttpClient\MockHttpClient;
|
||||
use Symfony\Component\HttpClient\Response\MockResponse;
|
||||
|
||||
class ModelClientTest extends TestCase
|
||||
{
|
||||
public function testItIsSupportingTheCorrectModel()
|
||||
{
|
||||
$client = new ModelClient(new MockHttpClient(), 'http://localhost:1234');
|
||||
|
||||
$this->assertTrue($client->supports(new Completions('test-model')));
|
||||
}
|
||||
|
||||
public function testItIsExecutingTheCorrectRequest()
|
||||
{
|
||||
$resultCallback = static function (string $method, string $url, array $options): MockResponse {
|
||||
self::assertSame('POST', $method);
|
||||
self::assertSame('http://localhost:1234/v1/chat/completions', $url);
|
||||
self::assertSame(
|
||||
'{"model":"test-model","messages":[{"role":"user","content":"Hello, world!"}]}',
|
||||
$options['body']
|
||||
);
|
||||
|
||||
return new MockResponse();
|
||||
};
|
||||
|
||||
$httpClient = new MockHttpClient([$resultCallback]);
|
||||
$client = new ModelClient($httpClient, 'http://localhost:1234');
|
||||
|
||||
$payload = [
|
||||
'model' => 'test-model',
|
||||
'messages' => [
|
||||
['role' => 'user', 'content' => 'Hello, world!'],
|
||||
],
|
||||
];
|
||||
|
||||
$client->request(new Completions('test-model'), $payload);
|
||||
}
|
||||
|
||||
public function testItMergesOptionsWithPayload()
|
||||
{
|
||||
$resultCallback = static function (string $method, string $url, array $options): MockResponse {
|
||||
self::assertSame('POST', $method);
|
||||
self::assertSame('http://localhost:1234/v1/chat/completions', $url);
|
||||
self::assertSame(
|
||||
'{"temperature":0.7,"model":"test-model","messages":[{"role":"user","content":"Hello, world!"}]}',
|
||||
$options['body']
|
||||
);
|
||||
|
||||
return new MockResponse();
|
||||
};
|
||||
|
||||
$httpClient = new MockHttpClient([$resultCallback]);
|
||||
$client = new ModelClient($httpClient, 'http://localhost:1234');
|
||||
|
||||
$payload = [
|
||||
'model' => 'test-model',
|
||||
'messages' => [
|
||||
['role' => 'user', 'content' => 'Hello, world!'],
|
||||
],
|
||||
];
|
||||
|
||||
$client->request(new Completions('test-model'), $payload, ['temperature' => 0.7]);
|
||||
}
|
||||
|
||||
public function testItUsesEventSourceHttpClient()
|
||||
{
|
||||
$httpClient = new MockHttpClient();
|
||||
$client = new ModelClient($httpClient, 'http://localhost:1234');
|
||||
|
||||
$reflection = new \ReflectionProperty($client, 'httpClient');
|
||||
|
||||
$this->assertInstanceOf(EventSourceHttpClient::class, $reflection->getValue($client));
|
||||
}
|
||||
|
||||
public function testItKeepsExistingEventSourceHttpClient()
|
||||
{
|
||||
$eventSourceHttpClient = new EventSourceHttpClient(new MockHttpClient());
|
||||
$client = new ModelClient($eventSourceHttpClient, 'http://localhost:1234');
|
||||
|
||||
$reflection = new \ReflectionProperty($client, 'httpClient');
|
||||
|
||||
$this->assertSame($eventSourceHttpClient, $reflection->getValue($client));
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\AI\Platform\Tests\Bridge\LmStudio\Completions;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\AI\Platform\Bridge\LmStudio\Completions;
|
||||
use Symfony\AI\Platform\Bridge\LmStudio\Completions\ResultConverter;
|
||||
|
||||
class ResultConverterTest extends TestCase
|
||||
{
|
||||
public function testItSupportsCompletionsModel()
|
||||
{
|
||||
$converter = new ResultConverter();
|
||||
|
||||
$this->assertTrue($converter->supports(new Completions('test-model')));
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\AI\Platform\Tests\Bridge\LmStudio\Embeddings;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\AI\Platform\Bridge\LmStudio\Embeddings;
|
||||
use Symfony\AI\Platform\Bridge\LmStudio\Embeddings\ModelClient;
|
||||
use Symfony\Component\HttpClient\MockHttpClient;
|
||||
use Symfony\Component\HttpClient\Response\MockResponse;
|
||||
|
||||
class ModelClientTest extends TestCase
|
||||
{
|
||||
public function testItIsSupportingTheCorrectModel()
|
||||
{
|
||||
$client = new ModelClient(new MockHttpClient(), 'http://localhost:1234');
|
||||
|
||||
$this->assertTrue($client->supports(new Embeddings('test-model')));
|
||||
}
|
||||
|
||||
public function testItIsExecutingTheCorrectRequest()
|
||||
{
|
||||
$resultCallback = static function (string $method, string $url, array $options): MockResponse {
|
||||
self::assertSame('POST', $method);
|
||||
self::assertSame('http://localhost:1234/v1/embeddings', $url);
|
||||
self::assertSame('{"model":"test-model","input":"Hello, world!"}', $options['body']);
|
||||
|
||||
return new MockResponse();
|
||||
};
|
||||
|
||||
$httpClient = new MockHttpClient([$resultCallback]);
|
||||
$client = new ModelClient($httpClient, 'http://localhost:1234');
|
||||
|
||||
$model = new Embeddings('test-model');
|
||||
|
||||
$client->request($model, 'Hello, world!');
|
||||
}
|
||||
|
||||
public function testItMergesOptionsWithPayload()
|
||||
{
|
||||
$resultCallback = static function (string $method, string $url, array $options): MockResponse {
|
||||
self::assertSame('POST', $method);
|
||||
self::assertSame('http://localhost:1234/v1/embeddings', $url);
|
||||
self::assertSame(
|
||||
'{"custom_option":"value","model":"test-model","input":"Hello, world!"}',
|
||||
$options['body']
|
||||
);
|
||||
|
||||
return new MockResponse();
|
||||
};
|
||||
|
||||
$httpClient = new MockHttpClient([$resultCallback]);
|
||||
$client = new ModelClient($httpClient, 'http://localhost:1234');
|
||||
|
||||
$model = new Embeddings('test-model');
|
||||
|
||||
$client->request($model, 'Hello, world!', ['custom_option' => 'value']);
|
||||
}
|
||||
|
||||
public function testItHandlesArrayInput()
|
||||
{
|
||||
$resultCallback = static function (string $method, string $url, array $options): MockResponse {
|
||||
self::assertSame('POST', $method);
|
||||
self::assertSame('http://localhost:1234/v1/embeddings', $url);
|
||||
self::assertSame('{"model":"test-model","input":["Hello","world"]}', $options['body']);
|
||||
|
||||
return new MockResponse();
|
||||
};
|
||||
|
||||
$httpClient = new MockHttpClient([$resultCallback]);
|
||||
$client = new ModelClient($httpClient, 'http://localhost:1234');
|
||||
|
||||
$model = new Embeddings('test-model');
|
||||
|
||||
$client->request($model, ['Hello', 'world']);
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\AI\Platform\Tests\Bridge\LmStudio\Embeddings;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\AI\Platform\Bridge\LmStudio\Embeddings;
|
||||
use Symfony\AI\Platform\Bridge\LmStudio\Embeddings\ResultConverter;
|
||||
use Symfony\AI\Platform\Exception\RuntimeException;
|
||||
use Symfony\AI\Platform\Result\RawHttpResult;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
class ResultConverterTest extends TestCase
|
||||
{
|
||||
public function testItConvertsAResponseToAVectorResult()
|
||||
{
|
||||
$result = $this->createStub(ResponseInterface::class);
|
||||
$result
|
||||
->method('toArray')
|
||||
->willReturn(
|
||||
json_decode(
|
||||
<<<'JSON'
|
||||
{
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"object": "embedding",
|
||||
"index": 0,
|
||||
"embedding": [0.3, 0.4, 0.4]
|
||||
},
|
||||
{
|
||||
"object": "embedding",
|
||||
"index": 1,
|
||||
"embedding": [0.0, 0.0, 0.2]
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON,
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
$vectorResult = (new ResultConverter())->convert(new RawHttpResult($result));
|
||||
$convertedContent = $vectorResult->getContent();
|
||||
|
||||
$this->assertCount(2, $convertedContent);
|
||||
|
||||
$this->assertSame([0.3, 0.4, 0.4], $convertedContent[0]->getData());
|
||||
$this->assertSame([0.0, 0.0, 0.2], $convertedContent[1]->getData());
|
||||
}
|
||||
|
||||
public function testItThrowsExceptionWhenResponseDoesNotContainData()
|
||||
{
|
||||
$result = $this->createStub(ResponseInterface::class);
|
||||
$result
|
||||
->method('toArray')
|
||||
->willReturn(['invalid' => 'response']);
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectExceptionMessage('Response does not contain data');
|
||||
|
||||
(new ResultConverter())->convert(new RawHttpResult($result));
|
||||
}
|
||||
|
||||
public function testItSupportsEmbeddingsModel()
|
||||
{
|
||||
$converter = new ResultConverter();
|
||||
|
||||
$this->assertTrue($converter->supports(new Embeddings('test-model')));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user