Skip to content
This repository was archived by the owner on Jul 16, 2025. It is now read-only.

Commit 7887f97

Browse files
committed
feat: prepare for release 0.8
1 parent ac666de commit 7887f97

File tree

4 files changed

+206
-153
lines changed

4 files changed

+206
-153
lines changed

README.md

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,26 @@ composer require php-llm/llm-chain-bundle
1414
# config/packages/llm_chain.yaml
1515
llm_chain:
1616
platforms:
17+
default:
18+
openai:
19+
api_key: '%env(OPENAI_API_KEY)%'
1720
azure_gpt:
18-
type: 'azure'
19-
base_url: '%env(AZURE_OPENAI_BASEURL)%'
20-
deployment: '%env(AZURE_OPENAI_GPT)%'
21-
api_key: '%env(AZURE_OPENAI_KEY)%'
22-
version: '%env(AZURE_OPENAI_VERSION)%'
21+
azure:
22+
base_url: '%env(AZURE_OPENAI_BASEURL)%'
23+
deployment: '%env(AZURE_OPENAI_GPT)%'
24+
api_key: '%env(AZURE_OPENAI_KEY)%'
25+
version: '%env(AZURE_OPENAI_VERSION)%'
2326
azure_embeddings:
24-
type: 'azure'
25-
base_url: '%env(AZURE_OPENAI_BASEURL)%'
26-
deployment: '%env(AZURE_OPENAI_EMBEDDINGS)%'
27-
api_key: '%env(AZURE_OPENAI_KEY)%'
28-
version: '%env(AZURE_OPENAI_VERSION)%'
29-
openai:
30-
type: 'openai'
31-
api_key: '%env(OPENAI_API_KEY)%'
32-
llms:
33-
azure_gpt:
27+
azure:
28+
base_url: '%env(AZURE_OPENAI_BASEURL)%'
29+
deployment: '%env(AZURE_OPENAI_EMBEDDINGS)%'
30+
api_key: '%env(AZURE_OPENAI_KEY)%'
31+
version: '%env(AZURE_OPENAI_VERSION)%'
32+
chains:
33+
default:
3434
platform: 'azure_gpt'
35-
original_gpt:
36-
platform: 'openai'
37-
embeddings:
38-
azure_embeddings:
39-
platform: 'azure_embeddings'
40-
original_embeddings:
41-
platform: 'openai'
35+
model: 'gpt'
36+
version: 'gpt-3.5-turbo'
4237
stores:
4338
azure_search:
4439
engine: 'azure-search'
@@ -99,18 +94,18 @@ services:
9994
autowire: true
10095
autoconfigure: true
10196
102-
PhpLlm\LlmChain\ToolBox\Tool\Clock: ~
103-
PhpLlm\LlmChain\ToolBox\Tool\OpenMeteo: ~
104-
PhpLlm\LlmChain\ToolBox\Tool\SerpApi:
97+
PhpLlm\LlmChain\Chain\ToolBox\Tool\Clock: ~
98+
PhpLlm\LlmChain\Chain\ToolBox\Tool\OpenMeteo: ~
99+
PhpLlm\LlmChain\Chain\ToolBox\Tool\SerpApi:
105100
$apiKey: '%env(SERP_API_KEY)%'
106-
PhpLlm\LlmChain\ToolBox\Tool\SimilaritySearch: ~
107-
PhpLlm\LlmChain\ToolBox\Tool\Wikipedia: ~
108-
PhpLlm\LlmChain\ToolBox\Tool\YouTubeTranscriber: ~
101+
PhpLlm\LlmChain\Chain\ToolBox\Tool\SimilaritySearch: ~
102+
PhpLlm\LlmChain\Chain\ToolBox\Tool\Wikipedia: ~
103+
PhpLlm\LlmChain\Chain\ToolBox\Tool\YouTubeTranscriber: ~
109104
```
110105

111106
Custom tools can be registered by using the `#[AsTool]` attribute:
112107
```php
113-
use PhpLlm\LlmChain\ToolBox\AsTool;
108+
use PhpLlm\LlmChain\Chain\ToolBox\Attribute\AsTool;
114109
115110
#[AsTool('company_name', 'Provides the name of your company')]
116111
final class CompanyName

src/DependencyInjection/Configuration.php

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,50 @@ public function getConfigTreeBuilder(): TreeBuilder
1616

1717
$rootNode
1818
->children()
19-
->arrayNode('platforms')
20-
->normalizeKeys(false)
21-
->useAttributeAsKey('name')
22-
->arrayPrototype()
23-
->children()
24-
->enumNode('type')->values(['openai', 'azure'])->isRequired()->end()
25-
->scalarNode('api_key')->isRequired()->end()
26-
->scalarNode('base_url')->end()
27-
->scalarNode('deployment')->end()
28-
->scalarNode('version')->end()
19+
->arrayNode('platform')
20+
->children()
21+
->arrayNode('openai')
22+
->children()
23+
->scalarNode('api_key')->isRequired()->end()
24+
->end()
25+
->end()
26+
->arrayNode('azure')
27+
->children()
28+
->scalarNode('api_key')->isRequired()->end()
29+
->scalarNode('base_url')->isRequired()->end()
30+
->scalarNode('deployment')->isRequired()->end()
31+
->scalarNode('version')->isRequired()->end()
32+
->end()
2933
->end()
3034
->end()
3135
->end()
32-
->arrayNode('llms')
36+
->arrayNode('chains')
3337
->normalizeKeys(false)
3438
->useAttributeAsKey('name')
3539
->arrayPrototype()
3640
->children()
37-
->scalarNode('platform')->end()
41+
->arrayNode('model')
42+
->children()
43+
->scalarNode('name')->isRequired()->end()
44+
->scalarNode('version')->defaultNull()->end()
45+
->arrayNode('options')->end()
46+
->end()
47+
->end()
3848
->end()
3949
->end()
4050
->end()
41-
->arrayNode('embeddings')
51+
->arrayNode('embedder')
4252
->normalizeKeys(false)
4353
->useAttributeAsKey('name')
4454
->arrayPrototype()
4555
->children()
46-
->scalarNode('platform')->end()
56+
->arrayNode('model')
57+
->children()
58+
->scalarNode('name')->isRequired()->end()
59+
->scalarNode('version')->defaultNull()->end()
60+
->arrayNode('options')->end()
61+
->end()
62+
->end()
4763
->end()
4864
->end()
4965
->end()
@@ -71,6 +87,7 @@ public function getConfigTreeBuilder(): TreeBuilder
7187
->scalarNode('top_k')->end()
7288
->end()
7389
->end()
90+
->end()
7491
->end()
7592
;
7693

src/DependencyInjection/LlmChainExtension.php

Lines changed: 77 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,27 @@
44

55
namespace PhpLlm\LlmChainBundle\DependencyInjection;
66

7+
use PhpLlm\LlmChain\Bridge\Anthropic\Claude;
8+
use PhpLlm\LlmChain\Bridge\Azure\Store\SearchStore as AzureSearchStore;
9+
use PhpLlm\LlmChain\Bridge\ChromaDB\Store as ChromaDBStore;
10+
use PhpLlm\LlmChain\Bridge\Meta\Llama;
11+
use PhpLlm\LlmChain\Bridge\MongoDB\Store as MongoDBStore;
12+
use PhpLlm\LlmChain\Bridge\OpenAI\Embeddings;
13+
use PhpLlm\LlmChain\Bridge\OpenAI\Embeddings\ModelClient as OpenAIEmbeddingsModelClient;
14+
use PhpLlm\LlmChain\Bridge\OpenAI\GPT;
15+
use PhpLlm\LlmChain\Bridge\OpenAI\GPT\ModelClient as OpenAIGPTModelClient;
16+
use PhpLlm\LlmChain\Bridge\Pinecone\Store as PineconeStore;
17+
use PhpLlm\LlmChain\Bridge\Voyage\Voyage;
18+
use PhpLlm\LlmChain\Chain;
719
use PhpLlm\LlmChain\Chain\InputProcessor;
820
use PhpLlm\LlmChain\Chain\OutputProcessor;
9-
use PhpLlm\LlmChain\EmbeddingsModel;
10-
use PhpLlm\LlmChain\LanguageModel;
11-
use PhpLlm\LlmChain\OpenAI\Model\Embeddings;
12-
use PhpLlm\LlmChain\OpenAI\Model\Gpt;
13-
use PhpLlm\LlmChain\OpenAI\Platform;
14-
use PhpLlm\LlmChain\OpenAI\Platform\Azure as AzurePlatform;
15-
use PhpLlm\LlmChain\OpenAI\Platform\OpenAI as OpenAIPlatform;
16-
use PhpLlm\LlmChain\Store\Azure\SearchStore as AzureSearchStore;
17-
use PhpLlm\LlmChain\Store\ChromaDB\Store as ChromaDBStore;
18-
use PhpLlm\LlmChain\Store\MongoDB\Store as MongoDBStore;
19-
use PhpLlm\LlmChain\Store\Pinecone\Store as PineconeStore;
21+
use PhpLlm\LlmChain\Chain\ToolBox\Attribute\AsTool;
22+
use PhpLlm\LlmChain\Embedder;
23+
use PhpLlm\LlmChain\Platform\ModelClient;
24+
use PhpLlm\LlmChain\Platform\ResponseConverter;
2025
use PhpLlm\LlmChain\Store\StoreInterface;
2126
use PhpLlm\LlmChain\Store\VectorStoreInterface;
22-
use PhpLlm\LlmChain\ToolBox\Attribute\AsTool;
2327
use PhpLlm\LlmChainBundle\Profiler\DataCollector;
24-
use PhpLlm\LlmChainBundle\Profiler\TraceableLanguageModel;
2528
use PhpLlm\LlmChainBundle\Profiler\TraceableToolBox;
2629
use Symfony\Component\Config\FileLocator;
2730
use Symfony\Component\DependencyInjection\ChildDefinition;
@@ -41,25 +44,27 @@ public function load(array $configs, ContainerBuilder $container): void
4144
$configuration = new Configuration();
4245
$config = $this->processConfiguration($configuration, $configs);
4346

44-
foreach ($config['platforms'] as $platformName => $platform) {
45-
$this->processPlatformConfig($platformName, $platform, $container);
46-
}
47-
if (1 === count($config['platforms']) && isset($platformName)) {
48-
$container->setAlias(Platform::class, 'llm_chain.platform.'.$platformName);
47+
if (isset($config['platform'])) {
48+
if (1 !== count($config['platform'])) {
49+
throw new \InvalidArgumentException('Only one platform at a time is supported.');
50+
}
51+
52+
$type = array_keys($config['platform']);
53+
$this->processPlatformConfig($type[0], $config['platform'][$type[0]], $container);
4954
}
5055

51-
foreach ($config['llms'] as $llmName => $llm) {
52-
$this->processLlmConfig($llmName, $llm, $container);
56+
foreach ($config['chains'] as $chainName => $chain) {
57+
$this->processChainConfig($chainName, $chain, $container);
5358
}
54-
if (1 === count($config['llms']) && isset($llmName)) {
55-
$container->setAlias(LanguageModel::class, 'llm_chain.llm.'.$llmName);
59+
if (1 === count($config['chains']) && isset($chainName)) {
60+
$container->setAlias(Chain::class, 'llm_chain.chain.'.$chainName);
5661
}
5762

58-
foreach ($config['embeddings'] as $embeddingsName => $embeddings) {
59-
$this->processEmbeddingsConfig($embeddingsName, $embeddings, $container);
63+
foreach ($config['embedder'] as $embedderName => $embedder) {
64+
$this->processEmbedderConfig($embedderName, $embedder, $container);
6065
}
61-
if (1 === count($config['embeddings']) && isset($embeddingsName)) {
62-
$container->setAlias(EmbeddingsModel::class, 'llm_chain.embeddings.'.$embeddingsName);
66+
if (1 === count($config['embedder']) && isset($embedderName)) {
67+
$container->setAlias(Embedder::class, 'llm_chain.embedder.'.$chainName);
6368
}
6469

6570
foreach ($config['stores'] as $storeName => $store) {
@@ -82,6 +87,10 @@ public function load(array $configs, ContainerBuilder $container): void
8287
->addTag('llm_chain.chain.input_processor');
8388
$container->registerForAutoconfiguration(OutputProcessor::class)
8489
->addTag('llm_chain.chain.output_processor');
90+
$container->registerForAutoconfiguration(ModelClient::class)
91+
->addTag('llm_chain.platform.model_client');
92+
$container->registerForAutoconfiguration(ResponseConverter::class)
93+
->addTag('llm_chain.platform.response_converter');
8594

8695
if (false === $container->getParameter('kernel.debug')) {
8796
$container->removeDefinition(DataCollector::class);
@@ -92,63 +101,65 @@ public function load(array $configs, ContainerBuilder $container): void
92101
/**
93102
* @param array<string, mixed> $platform
94103
*/
95-
private function processPlatformConfig(string $name, array $platform, ContainerBuilder $container): void
104+
private function processPlatformConfig(string $type, array $platform, ContainerBuilder $container): void
96105
{
97-
if ('openai' === $platform['type']) {
98-
$definition = new ChildDefinition(OpenAIPlatform::class);
99-
$definition
106+
if ('openai' === $type) {
107+
$container->getDefinition(OpenAIEmbeddingsModelClient::class)
100108
->replaceArgument('$apiKey', $platform['api_key']);
101109

102-
$container->setDefinition('llm_chain.platform.'.$name, $definition);
110+
$container->getDefinition(OpenAIGPTModelClient::class)
111+
->replaceArgument('$apiKey', $platform['api_key']);
103112

104113
return;
105114
}
106115

107-
if ('azure' === $platform['type']) {
108-
$definition = new ChildDefinition(AzurePlatform::class);
109-
$definition
110-
->replaceArgument('$baseUrl', $platform['base_url'])
111-
->replaceArgument('$deployment', $platform['deployment'])
112-
->replaceArgument('$apiKey', $platform['api_key'])
113-
->replaceArgument('$apiVersion', $platform['version']);
114-
115-
$container->setDefinition('llm_chain.platform.'.$name, $definition);
116-
}
116+
// TODO: Azure, Replicate, Ollama, etc.
117+
throw new \InvalidArgumentException(sprintf('Platform "%s" is not supported for configuration via bundle at this point.', $type));
117118
}
118119

119-
/**
120-
* @param array<string, mixed> $llm
121-
*/
122-
private function processLlmConfig(string $name, array $llm, ContainerBuilder $container): void
120+
private function processChainConfig(string $name, array $config, ContainerBuilder $container): void
123121
{
124-
$platform = isset($llm['platform']) ? 'llm_chain.platform.'.$llm['platform'] : Platform::class;
125-
126-
$definition = new ChildDefinition(Gpt::class);
127-
$definition->replaceArgument('$platform', new Reference($platform));
122+
['name' => $modelName, 'version' => $version, 'options' => $options] = $config['model'];
123+
124+
$llmClass = match (strtolower($modelName)) {
125+
'gpt' => GPT::class,
126+
'claude' => Claude::class,
127+
'llama' => Llama::class,
128+
default => throw new \InvalidArgumentException(sprintf('Model "%s" is not supported.', $modelName)),
129+
};
130+
$llmDefinition = (new Definition($llmClass))
131+
->setArguments([
132+
'$version' => $version,
133+
'$options' => $options,
134+
]);
135+
$container->setDefinition('llm_chain.chain.'.$name.'.llm', $llmDefinition);
128136

129-
$container->setDefinition('llm_chain.llm.'.$name, $definition);
137+
$definition = (new ChildDefinition('llm_chain.chain.abstract'))
138+
->replaceArgument('$llm', new Reference('llm_chain.chain.'.$name.'.llm'));
130139

131-
if ($container->getParameter('kernel.debug')) {
132-
$traceable = new Definition(TraceableLanguageModel::class);
133-
$traceable->setDecoratedService('llm_chain.llm.'.$name);
134-
$traceable->addTag('llm_chain.traceable_llm');
135-
$traceable->setArgument('$llm', new Reference('llm_chain.llm.'.$name.'.debug.inner'));
136-
$traceable->setArgument('$name', $name);
137-
$container->setDefinition('llm_chain.llm.'.$name.'.debug', $traceable);
138-
}
140+
$container->setDefinition('llm_chain.chain.'.$name, $definition);
139141
}
140142

141-
/**
142-
* @param array<string, mixed> $embeddings
143-
*/
144-
private function processEmbeddingsConfig(string $name, array $embeddings, ContainerBuilder $container): void
143+
private function processEmbedderConfig(int|string $name, mixed $config, ContainerBuilder $container)
145144
{
146-
$platform = isset($embeddings['platform']) ? 'llm_chain.platform.'.$embeddings['platform'] : Platform::class;
145+
['name' => $modelName, 'version' => $version, 'options' => $options] = $config['model'];
146+
147+
$modelClass = match (strtolower($modelName)) {
148+
'openai' => Embeddings::class,
149+
'voyage' => Voyage::class,
150+
default => throw new \InvalidArgumentException(sprintf('Model "%s" is not supported.', $modelName)),
151+
};
152+
$modelDefinition = (new Definition($modelClass))
153+
->setArguments([
154+
'$version' => $version,
155+
'$options' => $options,
156+
]);
157+
$container->setDefinition('llm_chain.embedder.'.$name.'.embeddings', $modelDefinition);
147158

148-
$definition = new ChildDefinition(Embeddings::class);
149-
$definition->replaceArgument('$platform', new Reference($platform));
159+
$definition = (new ChildDefinition('llm_chain.embedder.abstract'))
160+
->replaceArgument('$embeddings', new Reference('llm_chain.embedder.'.$name.'.embeddings'));
150161

151-
$container->setDefinition('llm_chain.embeddings.'.$name, $definition);
162+
$container->setDefinition('llm_chain.embedder.'.$name, $definition);
152163
}
153164

154165
/**

0 commit comments

Comments
 (0)