Skip to content

Commit bac56dc

Browse files
committed
Add Perplexity streamed response support
1 parent 0646f15 commit bac56dc

File tree

4 files changed

+94
-6
lines changed

4 files changed

+94
-6
lines changed

examples/perplexity/stream.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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+
use Symfony\AI\Agent\Agent;
13+
use Symfony\AI\Platform\Bridge\Perplexity\Perplexity;
14+
use Symfony\AI\Platform\Bridge\Perplexity\PlatformFactory;
15+
use Symfony\AI\Platform\Bridge\Perplexity\SearchResultProcessor;
16+
use Symfony\AI\Platform\Bridge\Perplexity\TokenOutputProcessor;
17+
use Symfony\AI\Platform\Message\Message;
18+
use Symfony\AI\Platform\Message\MessageBag;
19+
20+
require_once dirname(__DIR__) . '/bootstrap.php';
21+
22+
$platform = PlatformFactory::create(env('PERPLEXITY_API_KEY'), http_client());
23+
$model = new Perplexity();
24+
$agent = new Agent($platform, $model, outputProcessors: [new TokenOutputProcessor(), new SearchResultProcessor()], logger: logger());
25+
26+
$messages = new MessageBag(
27+
Message::forSystem('You are a thoughtful philosopher.'),
28+
Message::ofUser('What is the purpose of an ant?'),
29+
);
30+
$result = $agent->call($messages, [
31+
'stream' => true,
32+
]);
33+
34+
foreach ($result->getContent() as $word) {
35+
echo $word;
36+
}
37+
echo \PHP_EOL;
38+
39+
$metadata = $result->getMetadata();
40+
if ($metadata->has('search_results')) {
41+
echo 'Search results:'.\PHP_EOL;
42+
if (0 === count($metadata->get('search_results'))) {
43+
echo 'No search results.'.\PHP_EOL;
44+
45+
return;
46+
}
47+
foreach ($metadata->get('search_results') as $i => $searchResult) {
48+
echo 'Result #'.($i + 1).':'.\PHP_EOL;
49+
echo $searchResult['title'].\PHP_EOL;
50+
echo $searchResult['url'].\PHP_EOL;
51+
echo $searchResult['date'].\PHP_EOL;
52+
echo $searchResult['last_updated'] ? $searchResult['last_updated'].\PHP_EOL : '';
53+
echo $searchResult['snippet'] ? $searchResult['snippet'].\PHP_EOL : '';
54+
echo \PHP_EOL;
55+
}
56+
}
57+
58+
if ($metadata->has('citations')) {
59+
echo 'Citations:'.\PHP_EOL;
60+
if (0 === count($metadata->get('citations'))) {
61+
echo 'No citations.'.\PHP_EOL;
62+
63+
return;
64+
}
65+
foreach ($metadata->get('citations') as $i => $citation) {
66+
echo 'Citation #'.($i + 1).':'.\PHP_EOL;
67+
echo $citation.\PHP_EOL;
68+
echo \PHP_EOL;
69+
}
70+
}

src/platform/src/Bridge/Perplexity/ResultConverter.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\AI\Platform\Bridge\Perplexity;
1313

1414
use Symfony\AI\Platform\Exception\RuntimeException;
15+
use Symfony\AI\Platform\Metadata\Metadata;
1516
use Symfony\AI\Platform\Model;
1617
use Symfony\AI\Platform\Result\ChoiceResult;
1718
use Symfony\AI\Platform\Result\RawHttpResult;
@@ -56,6 +57,10 @@ public function convert(RawResultInterface|RawHttpResult $result, array $options
5657

5758
private function convertStream(HttpResponse $result): \Generator
5859
{
60+
$searchResults = $citations = [];
61+
/** @var Metadata $metadata */
62+
$metadata = yield;
63+
5964
foreach ((new EventSourceHttpClient())->stream($result) as $chunk) {
6065
if (!$chunk instanceof ServerSentEvent || '[DONE]' === $chunk->getData()) {
6166
continue;
@@ -68,12 +73,21 @@ private function convertStream(HttpResponse $result): \Generator
6873
continue;
6974
}
7075

71-
if (!isset($data['choices'][0]['delta']['content'])) {
72-
continue;
76+
if (isset($data['choices'][0]['delta']['content'])) {
77+
yield $data['choices'][0]['delta']['content'];
78+
}
79+
80+
if (isset($data['search_results'])) {
81+
$searchResults = $data['search_results'];
7382
}
7483

75-
yield $data['choices'][0]['delta']['content'];
84+
if (isset($data['citations'])) {
85+
$citations = $data['citations'];
86+
}
7687
}
88+
89+
$metadata->add('search_results', $searchResults);
90+
$metadata->add('citations', $citations);
7791
}
7892

7993
/**

src/platform/src/Bridge/Perplexity/SearchResultProcessor.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,13 @@ final class SearchResultProcessor implements OutputProcessorInterface
2323
{
2424
public function processOutput(Output $output): void
2525
{
26+
$metadata = $output->result->getMetadata();
27+
2628
if ($output->result instanceof StreamResult) {
27-
// @TODO
29+
$generator = $output->result->getContent();
30+
// Makes $metadata accessible in the stream loop.
31+
$generator->send($metadata);
32+
2833
return;
2934
}
3035

@@ -33,7 +38,6 @@ public function processOutput(Output $output): void
3338
return;
3439
}
3540

36-
$metadata = $output->result->getMetadata();
3741
$content = $rawResponse->toArray(false);
3842

3943
if (\array_key_exists('search_results', $content)) {

src/platform/src/Result/RawHttpResult.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use Symfony\Contracts\HttpClient\ResponseInterface;
1515

1616
/**
17-
* @author Christopher Hertel <mail@christopher-hertel.de
17+
* @author Christopher Hertel <mail@christopher-hertel.de>
1818
*/
1919
final readonly class RawHttpResult implements RawResultInterface
2020
{

0 commit comments

Comments
 (0)