Skip to content
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
28 changes: 28 additions & 0 deletions examples/scaleway/chat.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?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\Scaleway\PlatformFactory;
use Symfony\AI\Platform\Bridge\Scaleway\Scaleway;
use Symfony\AI\Platform\Message\Message;
use Symfony\AI\Platform\Message\MessageBag;

require_once dirname(__DIR__).'/bootstrap.php';

$platform = PlatformFactory::create(env('SCALEWAY_SECRET_KEY'), http_client());
$model = new Scaleway(Scaleway::OPENAI_OSS);

$messages = new MessageBag(
Message::forSystem('You are a pirate and you write funny.'),
Message::ofUser('What is the Symfony framework?'),
);
$result = $platform->invoke($model, $messages);

echo $result->asText().\PHP_EOL;
25 changes: 25 additions & 0 deletions examples/scaleway/embeddings.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?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\Scaleway\Embeddings;
use Symfony\AI\Platform\Bridge\Scaleway\PlatformFactory;

require_once dirname(__DIR__).'/bootstrap.php';

$platform = PlatformFactory::create(env('SCALEWAY_SECRET_KEY'), http_client());

$result = $platform->invoke(new Embeddings(), <<<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);
31 changes: 31 additions & 0 deletions examples/scaleway/stream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?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\Scaleway\PlatformFactory;
use Symfony\AI\Platform\Bridge\Scaleway\Scaleway;
use Symfony\AI\Platform\Message\Message;
use Symfony\AI\Platform\Message\MessageBag;

require_once dirname(__DIR__).'/bootstrap.php';

$platform = PlatformFactory::create(env('SCALEWAY_SECRET_KEY'), http_client());
$model = new Scaleway(Scaleway::OPENAI_OSS);

$messages = new MessageBag(
Message::forSystem('You are a pirate and you write funny.'),
Message::ofUser('What is the Symfony framework?'),
);
$result = $platform->invoke($model, $messages, ['stream' => true]);

foreach ($result->getResult()->getContent() as $word) {
echo $word;
}
echo \PHP_EOL;
33 changes: 33 additions & 0 deletions examples/scaleway/structured-output-math.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?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\Agent\Agent;
use Symfony\AI\Agent\StructuredOutput\AgentProcessor;
use Symfony\AI\Fixtures\StructuredOutput\MathReasoning;
use Symfony\AI\Platform\Bridge\Scaleway\PlatformFactory;
use Symfony\AI\Platform\Bridge\Scaleway\Scaleway;
use Symfony\AI\Platform\Message\Message;
use Symfony\AI\Platform\Message\MessageBag;

require_once dirname(__DIR__).'/bootstrap.php';

$platform = PlatformFactory::create(env('SCALEWAY_SECRET_KEY'), http_client());
$model = new Scaleway(Scaleway::OPENAI_OSS);

$processor = new AgentProcessor();
$agent = new Agent($platform, $model, [$processor], [$processor], logger: logger());
$messages = new MessageBag(
Message::forSystem('You are a helpful math tutor. Guide the user through the solution step by step.'),
Message::ofUser('how can I solve 8x + 7 = -23'),
);
$result = $agent->call($messages, ['output_structure' => MathReasoning::class]);

dump($result->getContent());
37 changes: 37 additions & 0 deletions examples/scaleway/toolcall-stream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?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\Agent\Agent;
use Symfony\AI\Agent\Toolbox\AgentProcessor;
use Symfony\AI\Agent\Toolbox\Tool\YouTubeTranscriber;
use Symfony\AI\Agent\Toolbox\Toolbox;
use Symfony\AI\Platform\Bridge\Scaleway\PlatformFactory;
use Symfony\AI\Platform\Bridge\Scaleway\Scaleway;
use Symfony\AI\Platform\Message\Message;
use Symfony\AI\Platform\Message\MessageBag;

require_once dirname(__DIR__).'/bootstrap.php';

$platform = PlatformFactory::create(env('SCALEWAY_SECRET_KEY'), http_client());
$model = new Scaleway(Scaleway::OPENAI_OSS);

$transcriber = new YouTubeTranscriber(http_client());
$toolbox = new Toolbox([$transcriber], logger: logger());
$processor = new AgentProcessor($toolbox);
$agent = new Agent($platform, $model, [$processor], [$processor], logger: logger());

$messages = new MessageBag(Message::ofUser('Please summarize this video for me: https://www.youtube.com/watch?v=6uXW-ulpj0s'));
$result = $agent->call($messages, ['stream' => true]);

foreach ($result->getContent() as $word) {
echo $word;
}
echo \PHP_EOL;
34 changes: 34 additions & 0 deletions examples/scaleway/toolcall.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?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\Agent\Agent;
use Symfony\AI\Agent\Toolbox\AgentProcessor;
use Symfony\AI\Agent\Toolbox\Tool\YouTubeTranscriber;
use Symfony\AI\Agent\Toolbox\Toolbox;
use Symfony\AI\Platform\Bridge\Scaleway\PlatformFactory;
use Symfony\AI\Platform\Bridge\Scaleway\Scaleway;
use Symfony\AI\Platform\Message\Message;
use Symfony\AI\Platform\Message\MessageBag;

require_once dirname(__DIR__).'/bootstrap.php';

$platform = PlatformFactory::create(env('SCALEWAY_SECRET_KEY'), http_client());
$model = new Scaleway(Scaleway::OPENAI_OSS);

$transcriber = new YouTubeTranscriber(http_client());
$toolbox = new Toolbox([$transcriber], logger: logger());
$processor = new AgentProcessor($toolbox);
$agent = new Agent($platform, $model, [$processor], [$processor], logger: logger());

$messages = new MessageBag(Message::ofUser('Please summarize this video for me: https://www.youtube.com/watch?v=6uXW-ulpj0s'));
$result = $agent->call($messages);

echo $result->getContent().\PHP_EOL;
31 changes: 31 additions & 0 deletions examples/scaleway/vision.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?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\Scaleway\PlatformFactory;
use Symfony\AI\Platform\Bridge\Scaleway\Scaleway;
use Symfony\AI\Platform\Message\Content\Image;
use Symfony\AI\Platform\Message\Message;
use Symfony\AI\Platform\Message\MessageBag;

require_once dirname(__DIR__).'/bootstrap.php';

$platform = PlatformFactory::create(env('SCALEWAY_SECRET_KEY'), http_client());
$model = new Scaleway(Scaleway::MISTRAL_PIXTRAL);

$messages = new MessageBag(
Message::ofUser(
'Describe this image in 1 sentence. What is the object in the image?',
Image::fromFile(dirname(__DIR__, 2).'/fixtures/image.jpg'),
),
);
$result = $platform->invoke($model, $messages);

echo $result->asText().\PHP_EOL;
9 changes: 9 additions & 0 deletions src/ai-bundle/config/options.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,15 @@
->end()
->end()
->end()
->arrayNode('scaleway')
->children()
->scalarNode('api_key')->isRequired()->end()
->stringNode('http_client')
->defaultValue('http_client')
->info('Service ID of the HTTP client to use')
->end()
->end()
->end()
->end()
->end()
->arrayNode('agent')
Expand Down
18 changes: 18 additions & 0 deletions src/ai-bundle/src/AiBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
use Symfony\AI\Platform\Bridge\OpenAi\PlatformFactory as OpenAiPlatformFactory;
use Symfony\AI\Platform\Bridge\OpenRouter\PlatformFactory as OpenRouterPlatformFactory;
use Symfony\AI\Platform\Bridge\Perplexity\PlatformFactory as PerplexityPlatformFactory;
use Symfony\AI\Platform\Bridge\Scaleway\PlatformFactory as ScalewayPlatformFactory;
use Symfony\AI\Platform\Bridge\VertexAi\PlatformFactory as VertexAiPlatformFactory;
use Symfony\AI\Platform\Bridge\Voyage\PlatformFactory as VoyagePlatformFactory;
use Symfony\AI\Platform\Exception\RuntimeException;
Expand Down Expand Up @@ -492,6 +493,23 @@ private function processPlatformConfig(string $type, array $platform, ContainerB
return;
}

if ('scaleway' === $type && isset($platform['api_key'])) {
$platformId = 'ai.platform.scaleway';
$definition = (new Definition(Platform::class))
->setFactory(ScalewayPlatformFactory::class.'::create')
->setLazy(true)
->addTag('proxy', ['interface' => PlatformInterface::class])
->setArguments([
$platform['api_key'],
new Reference('http_client', ContainerInterface::NULL_ON_INVALID_REFERENCE),
])
->addTag('ai.platform');

$container->setDefinition($platformId, $definition);

return;
}

throw new InvalidArgumentException(\sprintf('Platform "%s" is not supported for configuration via bundle at this point.', $type));
}

Expand Down
1 change: 1 addition & 0 deletions src/platform/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ CHANGELOG
- Perplexity (Sonar models, supporting search results)
- AI/ML API (language models and embeddings)
- Docker Model Runner (local model hosting)
- Scaleway (language models like OpenAI OSS, Llama 4, Qwen 3, and more)
* Add comprehensive message system with role-based messaging:
- `UserMessage` for user inputs with multi-modal content
- `SystemMessage` for system instructions
Expand Down
30 changes: 30 additions & 0 deletions src/platform/src/Bridge/Scaleway/Embeddings.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?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\Scaleway;

use Symfony\AI\Platform\Model;

/**
* @author Marcus Stöhr <marcus@fischteich.net>
*/
final class Embeddings extends Model
{
public const BAAI_BGE = 'bge-multilingual-gemma2';

/**
* @param array<string, mixed> $options
*/
public function __construct(string $name = self::BAAI_BGE, array $options = [])
{
parent::__construct($name, [], $options);
}
}
50 changes: 50 additions & 0 deletions src/platform/src/Bridge/Scaleway/Embeddings/ModelClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?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\Scaleway\Embeddings;

use Symfony\AI\Platform\Bridge\Scaleway\Embeddings;
use Symfony\AI\Platform\Exception\InvalidArgumentException;
use Symfony\AI\Platform\Model;
use Symfony\AI\Platform\ModelClientInterface as PlatformResponseFactory;
use Symfony\AI\Platform\Result\RawHttpResult;
use Symfony\Contracts\HttpClient\HttpClientInterface;

/**
* @author Marcus Stöhr <marcus@fischteich.net>
*/
final readonly class ModelClient implements PlatformResponseFactory
{
public function __construct(
private HttpClientInterface $httpClient,
#[\SensitiveParameter] private string $apiKey,
) {
if ('' === $apiKey) {
throw new InvalidArgumentException('The API key must not be empty.');
}
}

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', 'https://api.scaleway.ai/v1/embeddings', [
'auth_bearer' => $this->apiKey,
'json' => array_merge($options, [
'model' => $model->getName(),
'input' => $payload,
]),
]));
}
}
Loading