Skip to content

Commit 5a64eaa

Browse files
authored
refactor: Major Library Refactoring (#326)
- [x] Introducing global model class to be more agnostic with explicit `Capability` class - [x] Introducing Contract with Normalizers to solve Base Contract (OpenAI) vs. Model/Platform specific differences - [x] Reshape into three major components `Chain`, `Platform` and `Store` - [x] Adoption of Symfony-style for CS Fixer and `Interface` & `Exception` suffix Replaces #301 and #305 # Breaking Changes * Sorting into three main sub compontents `Platform`, `Chain` and `Store` * High level implementation also went into those components, see `ChainInterface` as example * `Model` namespace went into `Platform` * Bridges got sorted into `Platform` or `Store` * Tool metadata moved from `Chain` to `Platform\Contract` * Implementation of `JsonSerializable` was dropped in favor of Symfony's serializer/normalizer * Removal of `LanguageModel` and `EmbeddingsModel` interface in favor of base `Model` and `Capabilities` * Renaming of `StructuredResponse` to `ObjectResponse` * Slimming down the `supports()` method of `ModelClient` and `ResponseConverter` * Changing signature of `ModelClient` to already accepting the normalized request payload * Renaming interfaces to always contain the `Interface` suffix * Renaming exceptions to always contain the `Exception` suffix
1 parent 1e113b0 commit 5a64eaa

File tree

410 files changed

+6474
-4589
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

410 files changed

+6474
-4589
lines changed

.php-cs-fixer.dist.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
->setParallelConfig(ParallelConfigFactory::detect())
1212
->setRules([
1313
'@Symfony' => true,
14+
'@Symfony:risky' => true,
15+
'protected_to_private' => false,
16+
'declare_strict_types' => false,
1417
'heredoc_indentation' => ['indentation' => 'start_plus_one'],
1518
])
19+
->setRiskyAllowed(true)
1620
->setFinder($finder);

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,8 @@ ci: ci-stable
3232
ci-stable: deps-stable rector cs phpstan tests
3333

3434
ci-lowest: deps-low rector cs phpstan tests
35+
36+
fix-transformers:
37+
wget -P /tmp https://github.com/rindow/rindow-matlib/releases/download/1.1.1/rindow-matlib_1.1.1-24.04_amd64.deb
38+
dpkg-deb -x /tmp/rindow-matlib_1.1.1-24.04_amd64.deb /tmp/librindowmatlib_extracted
39+
cp /tmp/librindowmatlib_extracted/usr/lib/rindowmatlib-thread/librindowmatlib.so vendor/codewithkyrian/transformers/libs/librindowmatlib.so

README.md

Lines changed: 65 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ Those models are provided by different **platforms**, like OpenAI, Azure, Google
4545
#### Example Instantiation
4646

4747
```php
48-
use PhpLlm\LlmChain\Bridge\OpenAI\Embeddings;
49-
use PhpLlm\LlmChain\Bridge\OpenAI\GPT;
50-
use PhpLlm\LlmChain\Bridge\OpenAI\PlatformFactory;
48+
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\Embeddings;
49+
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\GPT;
50+
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\PlatformFactory;
5151

5252
// Platform: OpenAI
5353
$platform = PlatformFactory::create($_ENV['OPENAI_API_KEY']);
@@ -90,13 +90,13 @@ have different content types, like `Text`, `Image` or `Audio`.
9090
#### Example Chain call with messages
9191

9292
```php
93-
use PhpLlm\LlmChain\Chain;
94-
use PhpLlm\LlmChain\Model\Message\Message;
95-
use PhpLlm\LlmChain\Model\Message\MessageBag;
93+
use PhpLlm\LlmChain\Chain\Chain;
94+
use PhpLlm\LlmChain\Platform\Message\Message;
95+
use PhpLlm\LlmChain\Platform\Message\MessageBag;
9696

9797
// Platform & LLM instantiation
9898

99-
$chain = new Chain($platform, $llm);
99+
$chain = new Chain($platform, $model);
100100
$messages = new MessageBag(
101101
Message::forSystem('You are a helpful chatbot answering questions about LLM Chain.'),
102102
Message::ofUser('Hello, how are you?'),
@@ -146,6 +146,7 @@ Tools are services that can be called by the LLM to provide additional features
146146
Tool calling can be enabled by registering the processors in the chain:
147147

148148
```php
149+
use PhpLlm\LlmChain\Chain\Chain;
149150
use PhpLlm\LlmChain\Chain\Toolbox\ChainProcessor;
150151
use PhpLlm\LlmChain\Chain\Toolbox\Toolbox;
151152

@@ -156,7 +157,7 @@ $yourTool = new YourTool();
156157
$toolbox = Toolbox::create($yourTool);
157158
$toolProcessor = new ChainProcessor($toolbox);
158159

159-
$chain = new Chain($platform, $llm, inputProcessor: [$toolProcessor], outputProcessor: [$toolProcessor]);
160+
$chain = new Chain($platform, $model, inputProcessor: [$toolProcessor], outputProcessor: [$toolProcessor]);
160161
```
161162

162163
Custom tools can basically be any class, but must configure by the `#[AsTool]` attribute.
@@ -219,8 +220,8 @@ partially support by LLMs like GPT.
219220
To leverage this, configure the `#[With]` attribute on the method arguments of your tool:
220221

221222
```php
222-
use PhpLlm\LlmChain\Chain\JsonSchema\Attribute\With;
223223
use PhpLlm\LlmChain\Chain\Toolbox\Attribute\AsTool;
224+
use PhpLlm\LlmChain\Platform\Contract\JsonSchema\Attribute\With;
224225

225226
#[AsTool('my_tool', 'Example tool with parameters requirements.')]
226227
final class MyTool
@@ -252,10 +253,10 @@ attribute to the class is not possible in those cases, but you can explicitly re
252253

253254
```php
254255
use PhpLlm\LlmChain\Chain\Toolbox\Toolbox;
255-
use PhpLlm\LlmChain\Chain\Toolbox\MetadataFactory\MemoryFactory;
256+
use PhpLlm\LlmChain\Chain\Toolbox\ToolFactory\MemoryToolFactory;
256257
use Symfony\Component\Clock\Clock;
257258

258-
$metadataFactory = (new MemoryFactory())
259+
$metadataFactory = (new MemoryToolFactory())
259260
->addTool(Clock::class, 'clock', 'Get the current date and time', 'now');
260261
$toolbox = new Toolbox($metadataFactory, [new Clock()]);
261262
```
@@ -268,12 +269,12 @@ tools in the same chain - which even enables you to overwrite the pre-existing c
268269

269270
```php
270271
use PhpLlm\LlmChain\Chain\Toolbox\Toolbox;
271-
use PhpLlm\LlmChain\Chain\Toolbox\MetadataFactory\ChainFactory;
272-
use PhpLlm\LlmChain\Chain\Toolbox\MetadataFactory\MemoryFactory;
273-
use PhpLlm\LlmChain\Chain\Toolbox\MetadataFactory\ReflectionFactory;
272+
use PhpLlm\LlmChain\Chain\Toolbox\ToolFactory\ChainFactory;
273+
use PhpLlm\LlmChain\Chain\Toolbox\ToolFactory\MemoryToolFactory;
274+
use PhpLlm\LlmChain\Chain\Toolbox\ToolFactory\ReflectionToolFactory;
274275

275-
$reflectionFactory = new ReflectionFactory(); // Register tools with #[AsTool] attribute
276-
$metadataFactory = (new MemoryFactory()) // Register or overwrite tools explicitly
276+
$reflectionFactory = new ReflectionToolFactory(); // Register tools with #[AsTool] attribute
277+
$metadataFactory = (new MemoryToolFactory()) // Register or overwrite tools explicitly
277278
->addTool(...);
278279
$toolbox = new Toolbox(new ChainFactory($metadataFactory, $reflectionFactory), [...]);
279280
```
@@ -287,14 +288,14 @@ Similar to third-party tools, you can also use a chain as a tool in another chai
287288
complex logic or to reuse a chain in multiple places or hide sub-chains from the LLM.
288289

289290
```php
290-
use PhpLlm\LlmChain\Chain\Toolbox\MetadataFactory\MemoryFactory;
291+
use PhpLlm\LlmChain\Chain\Toolbox\ToolFactory\MemoryToolFactory;
291292
use PhpLlm\LlmChain\Chain\Toolbox\Toolbox;
292293
use PhpLlm\LlmChain\Chain\Toolbox\Tool\Chain;
293294

294295
// Chain was initialized before
295296

296297
$chainTool = new Chain($chain);
297-
$metadataFactory = (new MemoryFactory())
298+
$metadataFactory = (new MemoryToolFactory())
298299
->addTool($chainTool, 'research_agent', 'Meaningful description for sub-chain');
299300
$toolbox = new Toolbox($metadataFactory, [$chainTool]);
300301
```
@@ -306,6 +307,7 @@ To gracefully handle errors that occur during tool calling, e.g. wrong tool name
306307
to the LLM.
307308

308309
```php
310+
use PhpLlm\LlmChain\Chain\Chain;
309311
use PhpLlm\LlmChain\Chain\Toolbox\ChainProcessor;
310312
use PhpLlm\LlmChain\Chain\Toolbox\FaultTolerantToolbox;
311313

@@ -314,7 +316,7 @@ use PhpLlm\LlmChain\Chain\Toolbox\FaultTolerantToolbox;
314316
$toolbox = new FaultTolerantToolbox($innerToolbox);
315317
$toolProcessor = new ChainProcessor($toolbox);
316318

317-
$chain = new Chain($platform, $llm, inputProcessor: [$toolProcessor], outputProcessor: [$toolProcessor]);
319+
$chain = new Chain($platform, $model, inputProcessor: [$toolProcessor], outputProcessor: [$toolProcessor]);
318320
```
319321

320322
#### Tool Filtering
@@ -362,12 +364,11 @@ For populating a vector store, LLM Chain provides the service `Embedder`, which
362364
`EmbeddingsModel` and one of `StoreInterface`, and works with a collection of `Document` objects as input:
363365

364366
```php
365-
use PhpLlm\LlmChain\Embedder;
366-
use PhpLlm\LlmChain\Bridge\OpenAI\Embeddings;
367-
use PhpLlm\LlmChain\Bridge\OpenAI\PlatformFactory;
368-
use PhpLlm\LlmChain\Bridge\Pinecone\Store;
367+
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\Embeddings;
368+
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\PlatformFactory;
369+
use PhpLlm\LlmChain\Store\Bridge\Pinecone\Store;
370+
use PhpLlm\LlmChain\Store\Embedder;
369371
use Probots\Pinecone\Pinecone;
370-
use Symfony\Component\HttpClient\HttpClient;
371372

372373
$embedder = new Embedder(
373374
PlatformFactory::create($_ENV['OPENAI_API_KEY']),
@@ -380,8 +381,8 @@ $embedder->embed($documents);
380381
The collection of `Document` instances is usually created by text input of your domain entities:
381382

382383
```php
383-
use PhpLlm\LlmChain\Document\Metadata;
384-
use PhpLlm\LlmChain\Document\TextDocument;
384+
use PhpLlm\LlmChain\Store\Document\Metadata;
385+
use PhpLlm\LlmChain\Store\Document\TextDocument;
385386

386387
foreach ($entities as $entity) {
387388
$documents[] = new TextDocument(
@@ -399,19 +400,19 @@ In the end the chain is used in combination with a retrieval tool on top of the
399400
`SimilaritySearch` tool provided by the library:
400401

401402
```php
402-
use PhpLlm\LlmChain\Chain;
403-
use PhpLlm\LlmChain\Model\Message\Message;
404-
use PhpLlm\LlmChain\Model\Message\MessageBag;
403+
use PhpLlm\LlmChain\Chain\Chain;
405404
use PhpLlm\LlmChain\Chain\Toolbox\ChainProcessor;
406405
use PhpLlm\LlmChain\Chain\Toolbox\Tool\SimilaritySearch;
407406
use PhpLlm\LlmChain\Chain\Toolbox\Toolbox;
407+
use PhpLlm\LlmChain\Platform\Message\Message;
408+
use PhpLlm\LlmChain\Platform\Message\MessageBag;
408409

409410
// Initialize Platform & Models
410411

411412
$similaritySearch = new SimilaritySearch($embeddings, $store);
412413
$toolbox = Toolbox::create($similaritySearch);
413-
$processor = new ChainProcessor($toolbox);
414-
$chain = new Chain($platform, $llm, [$processor], [$processor]);
414+
$processor = new Chain($toolbox);
415+
$chain = new Chain($platform, $model, [$processor], [$processor]);
415416

416417
$messages = new MessageBag(
417418
Message::forSystem(<<<PROMPT
@@ -425,8 +426,8 @@ $response = $chain->call($messages);
425426
426427
#### Code Examples
427428
428-
1. [MongoDB Store](examples/store-mongodb-similarity-search.php)
429-
1. [Pinecone Store](examples/store-pinecone-similarity-search.php)
429+
1. [MongoDB Store](examples/store/mongodb-similarity-search.php)
430+
1. [Pinecone Store](examples/store/pinecone-similarity-search.php)
430431
431432
#### Supported Stores
432433
@@ -450,12 +451,13 @@ LLM Chain supports that use-case by abstracting the hustle of defining and provi
450451
the response back to PHP objects.
451452
452453
To achieve this, a specific chain processor needs to be registered:
454+
453455
```php
454-
use PhpLlm\LlmChain\Chain;
455-
use PhpLlm\LlmChain\Model\Message\Message;
456-
use PhpLlm\LlmChain\Model\Message\MessageBag;
456+
use PhpLlm\LlmChain\Chain\Chain;
457457
use PhpLlm\LlmChain\Chain\StructuredOutput\ChainProcessor;
458458
use PhpLlm\LlmChain\Chain\StructuredOutput\ResponseFormatFactory;
459+
use PhpLlm\LlmChain\Platform\Message\Message;
460+
use PhpLlm\LlmChain\Platform\Message\MessageBag;
459461
use PhpLlm\LlmChain\Tests\Chain\StructuredOutput\Data\MathReasoning;
460462
use Symfony\Component\Serializer\Encoder\JsonEncoder;
461463
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
@@ -465,7 +467,7 @@ use Symfony\Component\Serializer\Serializer;
465467
466468
$serializer = new Serializer([new ObjectNormalizer()], [new JsonEncoder()]);
467469
$processor = new ChainProcessor(new ResponseFormatFactory(), $serializer);
468-
$chain = new Chain($platform, $llm, [$processor], [$processor]);
470+
$chain = new Chain($platform, $model, [$processor], [$processor]);
469471
470472
$messages = new MessageBag(
471473
Message::forSystem('You are a helpful math tutor. Guide the user through the solution step by step.'),
@@ -481,8 +483,8 @@ dump($response->getContent()); // returns an instance of `MathReasoning` class
481483
Also PHP array structures as `response_format` are supported, which also requires the chain processor mentioned above:
482484
483485
```php
484-
use PhpLlm\LlmChain\Model\Message\Message;
485-
use PhpLlm\LlmChain\Model\Message\MessageBag;
486+
use PhpLlm\LlmChain\Platform\Message\Message;
487+
use PhpLlm\LlmChain\Platform\Message\MessageBag;
486488
487489
// Initialize Platform, LLM and Chain with processors and Clock tool
488490
@@ -518,13 +520,13 @@ Since LLMs usually generate a response word by word, most of them also support s
518520
Events. LLM Chain supports that by abstracting the conversion and returning a Generator as content of the response.
519521
520522
```php
521-
use PhpLlm\LlmChain\Chain;
523+
use PhpLlm\LlmChain\Chain\Chain;
522524
use PhpLlm\LlmChain\Message\Message;
523525
use PhpLlm\LlmChain\Message\MessageBag;
524526
525527
// Initialize Platform and LLM
526528
527-
$chain = new Chain($llm);
529+
$chain = new Chain($model);
528530
$messages = new MessageBag(
529531
Message::forSystem('You are a thoughtful philosopher.'),
530532
Message::ofUser('What is the purpose of an ant?'),
@@ -551,9 +553,9 @@ needs to be used.
551553
Some LLMs also support images as input, which LLM Chain supports as `Content` type within the `UserMessage`:
552554
553555
```php
554-
use PhpLlm\LlmChain\Model\Message\Content\Image;
555-
use PhpLlm\LlmChain\Model\Message\Message;
556-
use PhpLlm\LlmChain\Model\Message\MessageBag;
556+
use PhpLlm\LlmChain\Platform\Message\Content\Image;
557+
use PhpLlm\LlmChain\Platform\Message\Message;
558+
use PhpLlm\LlmChain\Platform\Message\MessageBag;
557559
558560
// Initialize Platform, LLM & Chain
559561
@@ -579,9 +581,9 @@ $response = $chain->call($messages);
579581
Similar to images, some LLMs also support audio as input, which is just another `Content` type within the `UserMessage`:
580582
581583
```php
582-
use PhpLlm\LlmChain\Model\Message\Content\Audio;
583-
use PhpLlm\LlmChain\Model\Message\Message;
584-
use PhpLlm\LlmChain\Model\Message\MessageBag;
584+
use PhpLlm\LlmChain\Platform\Message\Content\Audio;
585+
use PhpLlm\LlmChain\Platform\Message\Message;
586+
use PhpLlm\LlmChain\Platform\Message\MessageBag;
585587
586588
// Initialize Platform, LLM & Chain
587589
@@ -606,7 +608,7 @@ therefore LLM Chain implements a `EmbeddingsModel` interface with various models
606608
The standalone usage results in an `Vector` instance:
607609
608610
```php
609-
use PhpLlm\LlmChain\Bridge\OpenAI\Embeddings;
611+
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\Embeddings;
610612
611613
// Initialize Platform
612614
@@ -655,11 +657,11 @@ The behavior of the Chain is extendable with services that implement `InputProce
655657
interface. They are provided while instantiating the Chain instance:
656658
657659
```php
658-
use PhpLlm\LlmChain\Chain;
660+
use PhpLlm\LlmChain\Chain\Chain;
659661
660662
// Initialize Platform, LLM and processors
661663
662-
$chain = new Chain($platform, $llm, $inputProcessors, $outputProcessors);
664+
$chain = new Chain($platform, $model, $inputProcessors, $outputProcessors);
663665
```
664666
665667
#### InputProcessor
@@ -669,10 +671,10 @@ able to mutate both on top of the `Input` instance provided.
669671
670672
```php
671673
use PhpLlm\LlmChain\Chain\Input;
672-
use PhpLlm\LlmChain\Chain\InputProcessor;
673-
use PhpLlm\LlmChain\Model\Message\AssistantMessage
674+
use PhpLlm\LlmChain\Chain\InputProcessorInterface;
675+
use PhpLlm\LlmChain\Platform\Message\AssistantMessage;
674676
675-
final class MyProcessor implements InputProcessor
677+
final class MyProcessor implements InputProcessorInterface
676678
{
677679
public function processInput(Input $input): void
678680
{
@@ -694,10 +696,9 @@ mutate or replace the given response:
694696
695697
```php
696698
use PhpLlm\LlmChain\Chain\Output;
697-
use PhpLlm\LlmChain\Chain\OutputProcessor;
698-
use PhpLlm\LlmChain\Model\Message\AssistantMessage
699+
use PhpLlm\LlmChain\Chain\OutputProcessorInterface;
699700
700-
final class MyProcessor implements OutputProcessor
701+
final class MyProcessor implements OutputProcessorInterface
701702
{
702703
public function processOutput(Output $out): void
703704
{
@@ -716,13 +717,12 @@ provided, in case the processor implemented the `ChainAwareProcessor` interface,
716717
`ChainAwareTrait`:
717718
718719
```php
719-
use PhpLlm\LlmChain\Chain\ChainAwareProcessor;
720+
use PhpLlm\LlmChain\Chain\ChainAwareInterface;
720721
use PhpLlm\LlmChain\Chain\ChainAwareTrait;
721722
use PhpLlm\LlmChain\Chain\Output;
722-
use PhpLlm\LlmChain\Chain\OutputProcessor;
723-
use PhpLlm\LlmChain\Model\Message\AssistantMessage
723+
use PhpLlm\LlmChain\Chain\OutputProcessorInterface;
724724
725-
final class MyProcessor implements OutputProcessor, ChainAwareProcessor
725+
final class MyProcessor implements OutputProcessorInterface, ChainAwareInterface
726726
{
727727
use ChainAwareTrait;
728728
@@ -740,11 +740,12 @@ LLM Chain comes out of the box with an integration for [HuggingFace](https://hug
740740
hosting and sharing all kinds of models, including LLMs, embeddings, image generation, and classification models.
741741
742742
You can just instantiate the Platform with the corresponding HuggingFace bridge and use it with the `task` option:
743+
743744
```php
744745
use PhpLlm\LlmChain\Bridge\HuggingFace\Model;
745-
use PhpLlm\LlmChain\Bridge\HuggingFace\PlatformFactory;
746-
use PhpLlm\LlmChain\Bridge\HuggingFace\Task;
747-
use PhpLlm\LlmChain\Model\Message\Content\Image;
746+
use PhpLlm\LlmChain\Platform\Bridge\HuggingFace\PlatformFactory;
747+
use PhpLlm\LlmChain\Platform\Bridge\HuggingFace\Task;
748+
use PhpLlm\LlmChain\Platform\Message\Content\Image;
748749
749750
$platform = PlatformFactory::create($apiKey);
750751
$model = new Model('facebook/detr-resnet-50');
@@ -790,7 +791,7 @@ The usage with LLM Chain is similar to the HuggingFace integration, and also req
790791
```php
791792
use Codewithkyrian\Transformers\Pipelines\Task;
792793
use PhpLlm\LlmChain\Bridge\TransformersPHP\Model;
793-
use PhpLlm\LlmChain\Bridge\TransformersPHP\PlatformFactory;
794+
use PhpLlm\LlmChain\Platform\Bridge\TransformersPHP\PlatformFactory;
794795

795796
$platform = PlatformFactory::create();
796797
$model = new Model('Xenova/LaMini-Flan-T5-783M');

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"mongodb/mongodb": "^1.21",
4747
"php-cs-fixer/shim": "^3.70",
4848
"phpstan/phpstan": "^2.0",
49+
"phpstan/phpstan-symfony": "^2.0",
4950
"phpstan/phpstan-webmozart-assert": "^2.0",
5051
"phpunit/phpunit": "^11.5",
5152
"probots-io/pinecone-php": "^1.0",

0 commit comments

Comments
 (0)