Skip to content

chore: update to llm-chain 0.24 #29

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
merged 1 commit into from
Jul 6, 2025
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
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
"ext-iconv": "*",
"codewithkyrian/chromadb-php": "^0.4.0",
"league/commonmark": "^2.7",
"php-llm/llm-chain-bundle": "^0.22",
"mrmysql/youtube-transcript": "^0.0.5",
"php-http/discovery": "^1.20",
"php-llm/llm-chain-bundle": "^0.24",
"runtime/frankenphp-symfony": "^0.2.0",
"symfony/asset": "7.3.*",
"symfony/asset-mapper": "7.3.*",
Expand Down
585 changes: 373 additions & 212 deletions composer.lock

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions config/packages/http_discovery.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
services:
Psr\Http\Message\RequestFactoryInterface: '@http_discovery.psr17_factory'
Psr\Http\Message\ResponseFactoryInterface: '@http_discovery.psr17_factory'
Psr\Http\Message\ServerRequestFactoryInterface: '@http_discovery.psr17_factory'
Psr\Http\Message\StreamFactoryInterface: '@http_discovery.psr17_factory'
Psr\Http\Message\UploadedFileFactoryInterface: '@http_discovery.psr17_factory'
Psr\Http\Message\UriFactoryInterface: '@http_discovery.psr17_factory'

http_discovery.psr17_factory:
class: Http\Discovery\Psr17Factory
4 changes: 2 additions & 2 deletions config/packages/llm_chain.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ llm_chain:
chroma_db:
symfonycon:
collection: 'symfony_blog'
embedder:
indexer:
default:
model:
name: 'Embeddings'
Expand All @@ -61,5 +61,5 @@ services:
# $apiKey: '%env(SERP_API_KEY)%'
PhpLlm\LlmChain\Chain\Toolbox\Tool\Wikipedia: ~
PhpLlm\LlmChain\Chain\Toolbox\Tool\SimilaritySearch:
$model: '@llm_chain.embedder.default.model'
$model: '@llm_chain.indexer.default.model'

6 changes: 1 addition & 5 deletions src/Audio/Chat.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
use PhpLlm\LlmChain\Platform\Message\Message;
use PhpLlm\LlmChain\Platform\Message\MessageBag;
use PhpLlm\LlmChain\Platform\PlatformInterface;
use PhpLlm\LlmChain\Platform\Response\AsyncResponse;
use PhpLlm\LlmChain\Platform\Response\TextResponse;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\RequestStack;
Expand All @@ -34,11 +33,8 @@ public function say(string $base64audio): void
file_put_contents($path, base64_decode($base64audio));

$response = $this->platform->request(new Whisper(), Audio::fromFile($path));
assert($response instanceof AsyncResponse);
$response = $response->unwrap();
assert($response instanceof TextResponse);

$this->submitMessage($response->getContent());
$this->submitMessage($response->asText());
}

public function loadMessages(): MessageBag
Expand Down
7 changes: 1 addition & 6 deletions src/Blog/Command/QueryCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
use Codewithkyrian\ChromaDB\Client;
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\Embeddings;
use PhpLlm\LlmChain\Platform\PlatformInterface;
use PhpLlm\LlmChain\Platform\Response\AsyncResponse;
use PhpLlm\LlmChain\Platform\Response\VectorResponse;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
Expand Down Expand Up @@ -44,11 +42,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$io->comment('Results are limited to 4 most similar documents.');

$platformResponse = $this->platform->request(new Embeddings(), $search);
assert($platformResponse instanceof AsyncResponse);
$platformResponse = $platformResponse->unwrap();
assert($platformResponse instanceof VectorResponse);
$queryResponse = $collection->query(
queryEmbeddings: [$platformResponse->getContent()[0]->getData()],
queryEmbeddings: [$platformResponse->asVectors()[0]->getData()],
nResults: 4,
);

Expand Down
6 changes: 3 additions & 3 deletions src/Blog/Embedder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

use PhpLlm\LlmChain\Store\Document\Metadata;
use PhpLlm\LlmChain\Store\Document\TextDocument;
use PhpLlm\LlmChain\Store\Embedder as LlmChainEmbedder;
use PhpLlm\LlmChain\Store\Indexer;

final readonly class Embedder
{
public function __construct(
private FeedLoader $loader,
private LlmChainEmbedder $embedder,
private Indexer $indexer,
) {
}

Expand All @@ -23,6 +23,6 @@ public function embedBlog(): void
$documents[] = new TextDocument($post->id, $post->toString(), new Metadata($post->toArray()));
}

$this->embedder->embed($documents);
$this->indexer->index($documents);
}
}
8 changes: 1 addition & 7 deletions src/Video/TwigComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
use PhpLlm\LlmChain\Platform\Message\Message;
use PhpLlm\LlmChain\Platform\Message\MessageBag;
use PhpLlm\LlmChain\Platform\PlatformInterface;
use PhpLlm\LlmChain\Platform\Response\AsyncResponse;
use PhpLlm\LlmChain\Platform\Response\TextResponse;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveArg;
Expand Down Expand Up @@ -46,10 +44,6 @@ public function submit(#[LiveArg] string $instruction, #[LiveArg] string $image)
'max_tokens' => 100,
]);

assert($response instanceof AsyncResponse);
$response = $response->unwrap();
assert($response instanceof TextResponse);

$this->caption = $response->getContent();
$this->caption = $response->asText();
}
}
44 changes: 9 additions & 35 deletions src/YouTube/TranscriptFetcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

namespace App\YouTube;

use Symfony\Component\DomCrawler\Crawler;
use MrMySQL\YoutubeTranscript\TranscriptListFetcher;
use Symfony\Component\HttpClient\Psr18Client;
use Symfony\Contracts\HttpClient\HttpClientInterface;

final class TranscriptFetcher
Expand All @@ -16,41 +17,14 @@ public function __construct(

public function fetchTranscript(string $videoId): string
{
// Fetch the HTML content of the YouTube video page
$htmlResponse = $this->client->request('GET', 'https://youtube.com/watch?v='.$videoId);
$html = $htmlResponse->getContent();
$psr18Client = new Psr18Client($this->client);
$fetcher = new TranscriptListFetcher($psr18Client, $psr18Client, $psr18Client);

// Use DomCrawler to parse the HTML
$crawler = new Crawler($html);
$list = $fetcher->fetch($videoId);
$transcript = $list->findTranscript($list->getAvailableLanguageCodes());

// Extract the script containing the ytInitialPlayerResponse
$scriptContent = $crawler->filter('script')->reduce(function (Crawler $node) {
return str_contains($node->text(), 'var ytInitialPlayerResponse = {');
})->text();

// Extract and parse the JSON data from the script
$start = strpos($scriptContent, 'var ytInitialPlayerResponse = ') + strlen('var ytInitialPlayerResponse = ');
$dataString = substr($scriptContent, $start);
$dataString = substr($dataString, 0, strrpos($dataString, ';') ?: null);
$data = json_decode(trim($dataString), true);

// Extract the URL for the captions
if (!isset($data['captions']['playerCaptionsTracklistRenderer']['captionTracks'][0]['baseUrl'])) {
throw new \Exception('Captions are not available for this video.');
}
$captionsUrl = $data['captions']['playerCaptionsTracklistRenderer']['captionTracks'][0]['baseUrl'];

// Fetch and parse the captions XML
$xmlResponse = $this->client->request('GET', $captionsUrl);
$xmlContent = $xmlResponse->getContent();
$xmlCrawler = new Crawler($xmlContent);

// Collect all text elements from the captions
$transcript = $xmlCrawler->filter('text')->each(function (Crawler $node) {
return $node->text().' ';
});

// Combine all the text elements into one string
return implode(PHP_EOL, $transcript);
return array_reduce($transcript->fetch(), function (string $carry, array $item): string {
return $carry.\PHP_EOL.$item['text'];
}, '');
}
}
12 changes: 12 additions & 0 deletions symfony.lock
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@
".php-cs-fixer.dist.php"
]
},
"php-http/discovery": {
"version": "1.20",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.18",
"ref": "f45b5dd173a27873ab19f5e3180b2f661c21de02"
},
"files": [
"config/packages/http_discovery.yaml"
]
},
"php-llm/llm-chain-bundle": {
"version": "dev-main"
},
Expand Down
27 changes: 0 additions & 27 deletions tests/YouTube/TranscriptFetcherTest.php

This file was deleted.

66 changes: 0 additions & 66 deletions tests/YouTube/fixtures/transcript.xml

This file was deleted.

Loading