mirror of
https://github.com/symfony/ai-bundle.git
synced 2026-03-23 23:12:08 +01:00
refactor: integration llm-chain-bundle as ai-bundle into Symfony AI
* llm-bundle/restructure: (81 commits) refactor: restructure fix: support for 0.22 (#86) chore: support LLM Chain 0.22 (#85) chore: support LLM Chain 0.21 (#84) fix: profiler when using files as platform input (#81) fix: model name argument on bundle extension/injection (#80) chore(dependencies): require `php-llm/llm-chain` 0.20 (#78) fix: remove unwanted tools in system prompt by autowire (#77) feat: add inline tool definition and chain in chain 🤯 (#75) chore: 0.19 compatibility (#74) chore: lib update (#73) feat: support include tools feature to system prompt (#68) ci: streamline with lib (#71) style: add rector (#70) chore: prepare with release 0.17 (#69) feat: update for 0.16 - gemini & fault tolerant toolbox (#67) chore: lib update (#66) Fix: Embedder wiring (#64) chore: require `php-llm/llm-chain` 0.14 (#63) fix: require symfony/string explicitly (#62) ...
This commit is contained in:
6
.gitattributes
vendored
Normal file
6
.gitattributes
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/.github export-ignore
|
||||
/tests export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
phpstan.dist.neon export-ignore
|
||||
phpunit.xml.dist export-ignore
|
||||
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
Please do not submit any Pull Requests here. They will be closed.
|
||||
---
|
||||
|
||||
Please submit your PR here instead:
|
||||
https://github.com/symfony/ai
|
||||
|
||||
This repository is what we call a "subtree split": a read-only subset of that main repository.
|
||||
We're looking forward to your PR there!
|
||||
20
.github/workflows/close-pull-request.yml
vendored
Normal file
20
.github/workflows/close-pull-request.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Close Pull Request
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: superbrothers/close-pull-request@v3
|
||||
with:
|
||||
comment: |
|
||||
Thanks for your Pull Request! We love contributions.
|
||||
|
||||
However, you should instead open your PR on the main repository:
|
||||
https://github.com/symfony/ai
|
||||
|
||||
This repository is what we call a "subtree split": a read-only subset of that main repository.
|
||||
We're looking forward to your PR there!
|
||||
72
.github/workflows/pipeline.yml
vendored
72
.github/workflows/pipeline.yml
vendored
@@ -1,72 +0,0 @@
|
||||
name: pipeline
|
||||
on: pull_request
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
php: ['8.2', '8.3', '8.4']
|
||||
dependencies: ['lowest', 'highest']
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
|
||||
- name: Install Composer
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
dependency-versions: "${{ matrix.dependencies }}"
|
||||
|
||||
- name: Composer Validation
|
||||
run: composer validate --strict
|
||||
|
||||
- name: Install PHP Dependencies
|
||||
run: composer install --no-scripts
|
||||
|
||||
- name: Tests
|
||||
run: vendor/bin/phpunit
|
||||
|
||||
qa:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Conventional Commit
|
||||
uses: ytanikin/pr-conventional-commits@1.4.1
|
||||
with:
|
||||
task_types: '["feat", "fix", "docs", "test", "ci", "style", "refactor", "perf", "chore", "revert"]'
|
||||
add_label: 'true'
|
||||
custom_labels: '{"feat": "feature", "fix": "bug", "docs": "documentation", "test": "test", "ci": "CI/CD", "style": "codestyle", "refactor": "refactor", "perf": "performance", "chore": "chore", "revert": "revert"}'
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.2'
|
||||
|
||||
- name: Install Composer
|
||||
uses: "ramsey/composer-install@v3"
|
||||
|
||||
- name: Composer Validation
|
||||
run: composer validate --strict
|
||||
|
||||
- name: Install PHP Dependencies
|
||||
run: composer install --no-scripts
|
||||
|
||||
- name: Code Style PHP
|
||||
run: vendor/bin/php-cs-fixer fix --dry-run
|
||||
|
||||
- name: Rector
|
||||
run: vendor/bin/rector --dry-run
|
||||
|
||||
- name: PHPStan
|
||||
run: vendor/bin/phpstan analyse
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
$finder = (new PhpCsFixer\Finder())
|
||||
->in(__DIR__)
|
||||
;
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
->setRules([
|
||||
'@Symfony' => true,
|
||||
])
|
||||
->setFinder($finder)
|
||||
;
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2024 Christopher Hertel
|
||||
Copyright (c) 2025-present Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
37
Makefile
37
Makefile
@@ -1,37 +0,0 @@
|
||||
.PHONY: deps-stable deps-low cs rector phpstan tests coverage run-examples ci ci-stable ci-lowest
|
||||
|
||||
deps-stable:
|
||||
composer update --prefer-stable --ignore-platform-req=ext-mongodb
|
||||
|
||||
deps-low:
|
||||
composer update --prefer-lowest --ignore-platform-req=ext-mongodb
|
||||
|
||||
deps-dev:
|
||||
composer require php-llm/llm-chain:dev-main
|
||||
|
||||
cs:
|
||||
PHP_CS_FIXER_IGNORE_ENV=true vendor/bin/php-cs-fixer fix --diff --verbose
|
||||
|
||||
rector:
|
||||
vendor/bin/rector
|
||||
|
||||
phpstan:
|
||||
vendor/bin/phpstan --memory-limit=-1
|
||||
|
||||
tests:
|
||||
vendor/bin/phpunit
|
||||
|
||||
coverage:
|
||||
XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html=coverage
|
||||
|
||||
run-examples:
|
||||
./example
|
||||
|
||||
ci: ci-stable
|
||||
|
||||
ci-stable: deps-stable rector cs phpstan tests
|
||||
|
||||
ci-lowest: deps-low rector cs phpstan tests
|
||||
|
||||
ci-dev: deps-dev rector cs phpstan tests
|
||||
git restore composer.json
|
||||
100
README.md
100
README.md
@@ -1,11 +1,11 @@
|
||||
# LLM Chain Bundle
|
||||
# Symfony AI Bundle
|
||||
|
||||
Symfony integration bundle for [php-llm/llm-chain](https://github.com/php-llm/llm-chain) library.
|
||||
Integration bundle for Symfony AI components.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
composer require php-llm/llm-chain-bundle
|
||||
composer require symfony/ai-bundle
|
||||
```
|
||||
|
||||
## Configuration
|
||||
@@ -13,21 +13,21 @@ composer require php-llm/llm-chain-bundle
|
||||
### Simple Example with OpenAI
|
||||
|
||||
```yaml
|
||||
# config/packages/llm_chain.yaml
|
||||
llm_chain:
|
||||
# config/packages/ai.yaml
|
||||
ai:
|
||||
platform:
|
||||
openai:
|
||||
api_key: '%env(OPENAI_API_KEY)%'
|
||||
chain:
|
||||
agent:
|
||||
default:
|
||||
model:
|
||||
name: 'GPT'
|
||||
```
|
||||
|
||||
### Advanced Example with Anthropic, Azure, Google and multiple chains
|
||||
### Advanced Example with Anthropic, Azure, Google and multiple agents
|
||||
```yaml
|
||||
# config/packages/llm_chain.yaml
|
||||
llm_chain:
|
||||
# config/packages/ai.yaml
|
||||
ai:
|
||||
platform:
|
||||
anthropic:
|
||||
api_key: '%env(ANTHROPIC_API_KEY)%'
|
||||
@@ -40,36 +40,36 @@ llm_chain:
|
||||
api_version: '%env(AZURE_GPT_VERSION)%'
|
||||
google:
|
||||
api_key: '%env(GOOGLE_API_KEY)%'
|
||||
chain:
|
||||
agent:
|
||||
rag:
|
||||
platform: 'llm_chain.platform.azure.gpt_deployment'
|
||||
platform: 'symfony_ai.platform.azure.gpt_deployment'
|
||||
structured_output: false # Disables support for "output_structure" option, default is true
|
||||
model:
|
||||
name: 'GPT'
|
||||
version: 'gpt-4o-mini'
|
||||
system_prompt: 'You are a helpful assistant that can answer questions.' # The default system prompt of the chain
|
||||
include_tools: true # Include tool definitions at the end of the system prompt
|
||||
system_prompt: 'You are a helpful assistant that can answer questions.' # The default system prompt of the agent
|
||||
include_tools: true # Include tool definitions at the end of the system prompt
|
||||
tools:
|
||||
# Referencing a service with #[AsTool] attribute
|
||||
- 'PhpLlm\LlmChain\Chain\Toolbox\Tool\SimilaritySearch'
|
||||
|
||||
- 'Symfony\AI\Agent\Toolbox\Tool\SimilaritySearch'
|
||||
|
||||
# Referencing a service without #[AsTool] attribute
|
||||
- service: 'App\Chain\Tool\CompanyName'
|
||||
- service: 'App\Agent\Tool\CompanyName'
|
||||
name: 'company_name'
|
||||
description: 'Provides the name of your company'
|
||||
method: 'foo' # Optional with default value '__invoke'
|
||||
|
||||
# Referencing a chain => chain in chain 🤯
|
||||
- service: 'llm_chain.chain.research'
|
||||
|
||||
# Referencing a agent => agent in agent 🤯
|
||||
- service: 'symfony_ai.agent.research'
|
||||
name: 'wikipedia_research'
|
||||
description: 'Can research on Wikipedia'
|
||||
is_chain: true
|
||||
is_agent: true
|
||||
research:
|
||||
platform: 'llm_chain.platform.anthropic'
|
||||
platform: 'symfony_ai.platform.anthropic'
|
||||
model:
|
||||
name: 'Claude'
|
||||
tools: # If undefined, all tools are injected into the chain, use "tools: false" to disable tools.
|
||||
- 'PhpLlm\LlmChain\Chain\Toolbox\Tool\Wikipedia'
|
||||
tools: # If undefined, all tools are injected into the agent, use "tools: false" to disable tools.
|
||||
- 'Symfony\AI\Agent\Toolbox\Tool\Wikipedia'
|
||||
fault_tolerant_toolbox: false # Disables fault tolerant toolbox, default is true
|
||||
store:
|
||||
# also azure_search, mongodb and pinecone are supported as store type
|
||||
@@ -79,8 +79,8 @@ llm_chain:
|
||||
collection: 'my_collection'
|
||||
embedder:
|
||||
default:
|
||||
# platform: 'llm_chain.platform.anthropic'
|
||||
# store: 'llm_chain.store.chroma_db.default'
|
||||
# platform: 'symfony_ai.platform.anthropic'
|
||||
# store: 'symfony_ai.store.chroma_db.default'
|
||||
model:
|
||||
name: 'Embeddings'
|
||||
version: 'text-embedding-ada-002'
|
||||
@@ -88,21 +88,21 @@ llm_chain:
|
||||
|
||||
## Usage
|
||||
|
||||
### Chain Service
|
||||
### Agent Service
|
||||
|
||||
Use the `Chain` service to leverage GPT:
|
||||
Use the `Agent` service to leverage models and tools:
|
||||
```php
|
||||
use PhpLlm\LlmChain\ChainInterface;
|
||||
use PhpLlm\LlmChain\Model\Message\Message;
|
||||
use PhpLlm\LlmChain\Model\Message\MessageBag;
|
||||
use Symfony\AI\Agent\AgentInterface;
|
||||
use Symfony\AI\Platform\Message\Message;
|
||||
use Symfony\AI\Platform\Message\MessageBag;
|
||||
|
||||
final readonly class MyService
|
||||
{
|
||||
public function __construct(
|
||||
private ChainInterface $chain,
|
||||
private AgentInterface $agent,
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
public function submit(string $message): string
|
||||
{
|
||||
$messages = new MessageBag(
|
||||
@@ -110,7 +110,7 @@ final readonly class MyService
|
||||
Message::ofUser($message),
|
||||
);
|
||||
|
||||
return $this->chain->call($messages);
|
||||
return $this->agent->call($messages);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -124,21 +124,21 @@ services:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
PhpLlm\LlmChain\Chain\Toolbox\Tool\Clock: ~
|
||||
PhpLlm\LlmChain\Chain\Toolbox\Tool\OpenMeteo: ~
|
||||
PhpLlm\LlmChain\Chain\Toolbox\Tool\SerpApi:
|
||||
Symfony\AI\Agent\Toolbox\Tool\Clock: ~
|
||||
Symfony\AI\Agent\Toolbox\Tool\OpenMeteo: ~
|
||||
Symfony\AI\Agent\Toolbox\Tool\SerpApi:
|
||||
$apiKey: '%env(SERP_API_KEY)%'
|
||||
PhpLlm\LlmChain\Chain\Toolbox\Tool\SimilaritySearch: ~
|
||||
PhpLlm\LlmChain\Chain\Toolbox\Tool\Tavily:
|
||||
Symfony\AI\Agent\Toolbox\Tool\SimilaritySearch: ~
|
||||
Symfony\AI\Agent\Toolbox\Tool\Tavily:
|
||||
$apiKey: '%env(TAVILY_API_KEY)%'
|
||||
PhpLlm\LlmChain\Chain\Toolbox\Tool\Wikipedia: ~
|
||||
PhpLlm\LlmChain\Chain\Toolbox\Tool\YouTubeTranscriber: ~
|
||||
Symfony\AI\Agent\Toolbox\Tool\Wikipedia: ~
|
||||
Symfony\AI\Agent\Toolbox\Tool\YouTubeTranscriber: ~
|
||||
```
|
||||
|
||||
Custom tools can be registered by using the `#[AsTool]` attribute:
|
||||
|
||||
```php
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\Attribute\AsTool;
|
||||
use Symfony\AI\Agent\Toolbox\Attribute\AsTool;
|
||||
|
||||
#[AsTool('company_name', 'Provides the name of your company')]
|
||||
final class CompanyName
|
||||
@@ -150,27 +150,27 @@ final class CompanyName
|
||||
}
|
||||
```
|
||||
|
||||
The chain configuration by default will inject all known tools into the chain.
|
||||
The agent configuration by default will inject all known tools into the agent.
|
||||
|
||||
To disable this behavior, set the `tools` option to `false`:
|
||||
```yaml
|
||||
llm_chain:
|
||||
chain:
|
||||
my_chain:
|
||||
ai:
|
||||
agent:
|
||||
my_agent:
|
||||
tools: false
|
||||
```
|
||||
|
||||
To inject only specific tools, list them in the configuration:
|
||||
```yaml
|
||||
llm_chain:
|
||||
chain:
|
||||
my_chain:
|
||||
ai:
|
||||
agent:
|
||||
my_agent:
|
||||
tools:
|
||||
- 'PhpLlm\LlmChain\Chain\Toolbox\Tool\SimilaritySearch'
|
||||
- 'Symfony\AI\Agent\Toolbox\Tool\SimilaritySearch'
|
||||
```
|
||||
|
||||
### Profiler
|
||||
|
||||
The profiler panel provides insights into the chain's execution:
|
||||
The profiler panel provides insights into the agent's execution:
|
||||
|
||||

|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "php-llm/llm-chain-bundle",
|
||||
"name": "symfony/ai-bundle",
|
||||
"type": "symfony-bundle",
|
||||
"description": "Symfony integration bundle for php-llm/llm-chain",
|
||||
"description": "Integration bundle for Symfony AI components",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
@@ -15,29 +15,34 @@
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"php-llm/llm-chain": "^0.22",
|
||||
"symfony/ai-agent": "@dev",
|
||||
"symfony/ai-platform": "@dev",
|
||||
"symfony/ai-store": "@dev",
|
||||
"symfony/config": "^6.4 || ^7.0",
|
||||
"symfony/dependency-injection": "^6.4 || ^7.0",
|
||||
"symfony/framework-bundle": "^6.4 || ^7.0",
|
||||
"symfony/string": "^6.4 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-cs-fixer/shim": "^3.69",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "^11.5",
|
||||
"rector/rector": "^2.0"
|
||||
"phpunit/phpunit": "^11.5"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpLlm\\LlmChainBundle\\": "src/"
|
||||
"Symfony\\AI\\AIBundle\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"PhpLlm\\LlmChainBundle\\Tests\\": "tests/"
|
||||
"Symfony\\AI\\AIBundle\\Tests\\": "tests/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{"type": "path", "url": "../agent"},
|
||||
{"type": "path", "url": "../platform"},
|
||||
{"type": "path", "url": "../store"}
|
||||
]
|
||||
}
|
||||
|
||||
30
rector.php
30
rector.php
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Config\RectorConfig;
|
||||
use Rector\Php74\Rector\Closure\ClosureToArrowFunctionRector;
|
||||
use Rector\PHPUnit\CodeQuality\Rector\Class_\PreferPHPUnitSelfCallRector;
|
||||
use Rector\PHPUnit\CodeQuality\Rector\Class_\PreferPHPUnitThisCallRector;
|
||||
use Rector\PHPUnit\Set\PHPUnitSetList;
|
||||
|
||||
return RectorConfig::configure()
|
||||
->withPaths([
|
||||
__DIR__.'/src',
|
||||
__DIR__.'/tests',
|
||||
])
|
||||
->withPhpSets(php82: true)
|
||||
->withSets([
|
||||
PHPUnitSetList::PHPUNIT_110,
|
||||
PHPUnitSetList::ANNOTATIONS_TO_ATTRIBUTES,
|
||||
PHPUnitSetList::PHPUNIT_CODE_QUALITY,
|
||||
])
|
||||
->withRules([
|
||||
PreferPHPUnitSelfCallRector::class,
|
||||
])
|
||||
->withImportNames(importShortClasses: false)
|
||||
->withSkip([
|
||||
ClosureToArrowFunctionRector::class,
|
||||
PreferPHPUnitThisCallRector::class,
|
||||
])
|
||||
->withTypeCoverageLevel(0);
|
||||
21
src/AIBundle.php
Normal file
21
src/AIBundle.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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\AIBundle;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
/**
|
||||
* @author Christopher Hertel <mail@christopher-hertel.de>
|
||||
*/
|
||||
final class AIBundle extends Bundle
|
||||
{
|
||||
}
|
||||
@@ -1,46 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* 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 PhpLlm\LlmChainBundle\DependencyInjection;
|
||||
namespace Symfony\AI\AIBundle\DependencyInjection;
|
||||
|
||||
use PhpLlm\LlmChain\Chain\Chain;
|
||||
use PhpLlm\LlmChain\Chain\ChainInterface;
|
||||
use PhpLlm\LlmChain\Chain\InputProcessor\SystemPromptInputProcessor;
|
||||
use PhpLlm\LlmChain\Chain\InputProcessorInterface;
|
||||
use PhpLlm\LlmChain\Chain\OutputProcessorInterface;
|
||||
use PhpLlm\LlmChain\Chain\StructuredOutput\ChainProcessor as StructureOutputProcessor;
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\Attribute\AsTool;
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\ChainProcessor as ToolProcessor;
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\FaultTolerantToolbox;
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\Tool\Chain as ChainTool;
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\ToolFactory\ChainFactory;
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\ToolFactory\MemoryToolFactory;
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\ToolFactory\ReflectionToolFactory;
|
||||
use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Claude;
|
||||
use PhpLlm\LlmChain\Platform\Bridge\Anthropic\PlatformFactory as AnthropicPlatformFactory;
|
||||
use PhpLlm\LlmChain\Platform\Bridge\Azure\OpenAI\PlatformFactory as AzureOpenAIPlatformFactory;
|
||||
use PhpLlm\LlmChain\Platform\Bridge\Google\Gemini;
|
||||
use PhpLlm\LlmChain\Platform\Bridge\Google\PlatformFactory as GooglePlatformFactory;
|
||||
use PhpLlm\LlmChain\Platform\Bridge\Meta\Llama;
|
||||
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\Embeddings;
|
||||
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\GPT;
|
||||
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\PlatformFactory as OpenAIPlatformFactory;
|
||||
use PhpLlm\LlmChain\Platform\Bridge\Voyage\Voyage;
|
||||
use PhpLlm\LlmChain\Platform\ModelClientInterface;
|
||||
use PhpLlm\LlmChain\Platform\Platform;
|
||||
use PhpLlm\LlmChain\Platform\PlatformInterface;
|
||||
use PhpLlm\LlmChain\Platform\ResponseConverterInterface;
|
||||
use PhpLlm\LlmChain\Store\Bridge\Azure\SearchStore as AzureSearchStore;
|
||||
use PhpLlm\LlmChain\Store\Bridge\ChromaDB\Store as ChromaDBStore;
|
||||
use PhpLlm\LlmChain\Store\Bridge\MongoDB\Store as MongoDBStore;
|
||||
use PhpLlm\LlmChain\Store\Bridge\Pinecone\Store as PineconeStore;
|
||||
use PhpLlm\LlmChain\Store\Embedder;
|
||||
use PhpLlm\LlmChain\Store\StoreInterface;
|
||||
use PhpLlm\LlmChain\Store\VectorStoreInterface;
|
||||
use PhpLlm\LlmChainBundle\Profiler\DataCollector;
|
||||
use PhpLlm\LlmChainBundle\Profiler\TraceablePlatform;
|
||||
use PhpLlm\LlmChainBundle\Profiler\TraceableToolbox;
|
||||
use Symfony\AI\Agent\Agent;
|
||||
use Symfony\AI\Agent\AgentInterface;
|
||||
use Symfony\AI\Agent\InputProcessor\SystemPromptInputProcessor;
|
||||
use Symfony\AI\Agent\InputProcessorInterface;
|
||||
use Symfony\AI\Agent\OutputProcessorInterface;
|
||||
use Symfony\AI\Agent\StructuredOutput\AgentProcessor as StructureOutputProcessor;
|
||||
use Symfony\AI\Agent\Toolbox\AgentProcessor as ToolProcessor;
|
||||
use Symfony\AI\Agent\Toolbox\Attribute\AsTool;
|
||||
use Symfony\AI\Agent\Toolbox\FaultTolerantToolbox;
|
||||
use Symfony\AI\Agent\Toolbox\Tool\Agent as AgentTool;
|
||||
use Symfony\AI\Agent\Toolbox\ToolFactory\ChainFactory;
|
||||
use Symfony\AI\Agent\Toolbox\ToolFactory\MemoryToolFactory;
|
||||
use Symfony\AI\Agent\Toolbox\ToolFactory\ReflectionToolFactory;
|
||||
use Symfony\AI\AIBundle\Profiler\DataCollector;
|
||||
use Symfony\AI\AIBundle\Profiler\TraceablePlatform;
|
||||
use Symfony\AI\AIBundle\Profiler\TraceableToolbox;
|
||||
use Symfony\AI\Platform\Bridge\Anthropic\Claude;
|
||||
use Symfony\AI\Platform\Bridge\Anthropic\PlatformFactory as AnthropicPlatformFactory;
|
||||
use Symfony\AI\Platform\Bridge\Azure\OpenAI\PlatformFactory as AzureOpenAIPlatformFactory;
|
||||
use Symfony\AI\Platform\Bridge\Google\Gemini;
|
||||
use Symfony\AI\Platform\Bridge\Google\PlatformFactory as GooglePlatformFactory;
|
||||
use Symfony\AI\Platform\Bridge\Meta\Llama;
|
||||
use Symfony\AI\Platform\Bridge\OpenAI\Embeddings;
|
||||
use Symfony\AI\Platform\Bridge\OpenAI\GPT;
|
||||
use Symfony\AI\Platform\Bridge\OpenAI\PlatformFactory as OpenAIPlatformFactory;
|
||||
use Symfony\AI\Platform\Bridge\Voyage\Voyage;
|
||||
use Symfony\AI\Platform\ModelClientInterface;
|
||||
use Symfony\AI\Platform\Platform;
|
||||
use Symfony\AI\Platform\PlatformInterface;
|
||||
use Symfony\AI\Platform\ResponseConverterInterface;
|
||||
use Symfony\AI\Store\Bridge\Azure\SearchStore as AzureSearchStore;
|
||||
use Symfony\AI\Store\Bridge\ChromaDB\Store as ChromaDBStore;
|
||||
use Symfony\AI\Store\Bridge\MongoDB\Store as MongoDBStore;
|
||||
use Symfony\AI\Store\Bridge\Pinecone\Store as PineconeStore;
|
||||
use Symfony\AI\Store\Embedder;
|
||||
use Symfony\AI\Store\StoreInterface;
|
||||
use Symfony\AI\Store\VectorStoreInterface;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
@@ -51,11 +58,14 @@ use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
use function Symfony\Component\String\u;
|
||||
|
||||
final class LlmChainExtension extends Extension
|
||||
/**
|
||||
* @author Christopher Hertel <mail@christopher-hertel.de>
|
||||
*/
|
||||
final class AIExtension extends Extension
|
||||
{
|
||||
public function load(array $configs, ContainerBuilder $container): void
|
||||
{
|
||||
$loader = new PhpFileLoader($container, new FileLocator(dirname(__DIR__).'/Resources/config'));
|
||||
$loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/Resources/config'));
|
||||
$loader->load('services.php');
|
||||
|
||||
$configuration = new Configuration();
|
||||
@@ -63,8 +73,8 @@ final class LlmChainExtension extends Extension
|
||||
foreach ($config['platform'] ?? [] as $type => $platform) {
|
||||
$this->processPlatformConfig($type, $platform, $container);
|
||||
}
|
||||
$platforms = array_keys($container->findTaggedServiceIds('llm_chain.platform'));
|
||||
if (1 === count($platforms)) {
|
||||
$platforms = array_keys($container->findTaggedServiceIds('symfony_ai.platform'));
|
||||
if (1 === \count($platforms)) {
|
||||
$container->setAlias(PlatformInterface::class, reset($platforms));
|
||||
}
|
||||
if ($container->getParameter('kernel.debug')) {
|
||||
@@ -72,24 +82,24 @@ final class LlmChainExtension extends Extension
|
||||
$traceablePlatformDefinition = (new Definition(TraceablePlatform::class))
|
||||
->setDecoratedService($platform)
|
||||
->setAutowired(true)
|
||||
->addTag('llm_chain.traceable_platform');
|
||||
->addTag('symfony_ai.traceable_platform');
|
||||
$suffix = u($platform)->afterLast('.')->toString();
|
||||
$container->setDefinition('llm_chain.traceable_platform.'.$suffix, $traceablePlatformDefinition);
|
||||
$container->setDefinition('symfony_ai.traceable_platform.'.$suffix, $traceablePlatformDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($config['chain'] as $chainName => $chain) {
|
||||
$this->processChainConfig($chainName, $chain, $container);
|
||||
foreach ($config['agent'] as $agentName => $agent) {
|
||||
$this->processAgentConfig($agentName, $agent, $container);
|
||||
}
|
||||
if (1 === count($config['chain']) && isset($chainName)) {
|
||||
$container->setAlias(ChainInterface::class, 'llm_chain.chain.'.$chainName);
|
||||
if (1 === \count($config['agent']) && isset($agentName)) {
|
||||
$container->setAlias(AgentInterface::class, 'symfony_ai.agent.'.$agentName);
|
||||
}
|
||||
|
||||
foreach ($config['store'] ?? [] as $type => $store) {
|
||||
$this->processStoreConfig($type, $store, $container);
|
||||
}
|
||||
$stores = array_keys($container->findTaggedServiceIds('llm_chain.store'));
|
||||
if (1 === count($stores)) {
|
||||
$stores = array_keys($container->findTaggedServiceIds('symfony_ai.store'));
|
||||
if (1 === \count($stores)) {
|
||||
$container->setAlias(VectorStoreInterface::class, reset($stores));
|
||||
$container->setAlias(StoreInterface::class, reset($stores));
|
||||
}
|
||||
@@ -97,12 +107,12 @@ final class LlmChainExtension extends Extension
|
||||
foreach ($config['embedder'] as $embedderName => $embedder) {
|
||||
$this->processEmbedderConfig($embedderName, $embedder, $container);
|
||||
}
|
||||
if (1 === count($config['embedder']) && isset($embedderName)) {
|
||||
$container->setAlias(Embedder::class, 'llm_chain.embedder.'.$embedderName);
|
||||
if (1 === \count($config['embedder']) && isset($embedderName)) {
|
||||
$container->setAlias(Embedder::class, 'symfony_ai.embedder.'.$embedderName);
|
||||
}
|
||||
|
||||
$container->registerAttributeForAutoconfiguration(AsTool::class, static function (ChildDefinition $definition, AsTool $attribute): void {
|
||||
$definition->addTag('llm_chain.tool', [
|
||||
$definition->addTag('symfony_ai.tool', [
|
||||
'name' => $attribute->name,
|
||||
'description' => $attribute->description,
|
||||
'method' => $attribute->method,
|
||||
@@ -110,13 +120,13 @@ final class LlmChainExtension extends Extension
|
||||
});
|
||||
|
||||
$container->registerForAutoconfiguration(InputProcessorInterface::class)
|
||||
->addTag('llm_chain.chain.input_processor');
|
||||
->addTag('symfony_ai.agent.input_processor');
|
||||
$container->registerForAutoconfiguration(OutputProcessorInterface::class)
|
||||
->addTag('llm_chain.chain.output_processor');
|
||||
->addTag('symfony_ai.agent.output_processor');
|
||||
$container->registerForAutoconfiguration(ModelClientInterface::class)
|
||||
->addTag('llm_chain.platform.model_client');
|
||||
->addTag('symfony_ai.platform.model_client');
|
||||
$container->registerForAutoconfiguration(ResponseConverterInterface::class)
|
||||
->addTag('llm_chain.platform.response_converter');
|
||||
->addTag('symfony_ai.platform.response_converter');
|
||||
|
||||
if (false === $container->getParameter('kernel.debug')) {
|
||||
$container->removeDefinition(DataCollector::class);
|
||||
@@ -130,7 +140,7 @@ final class LlmChainExtension extends Extension
|
||||
private function processPlatformConfig(string $type, array $platform, ContainerBuilder $container): void
|
||||
{
|
||||
if ('anthropic' === $type) {
|
||||
$platformId = 'llm_chain.platform.anthropic';
|
||||
$platformId = 'symfony_ai.platform.anthropic';
|
||||
$definition = (new Definition(Platform::class))
|
||||
->setFactory(AnthropicPlatformFactory::class.'::create')
|
||||
->setAutowired(true)
|
||||
@@ -139,7 +149,7 @@ final class LlmChainExtension extends Extension
|
||||
->setArguments([
|
||||
'$apiKey' => $platform['api_key'],
|
||||
])
|
||||
->addTag('llm_chain.platform');
|
||||
->addTag('symfony_ai.platform');
|
||||
|
||||
if (isset($platform['version'])) {
|
||||
$definition->replaceArgument('$version', $platform['version']);
|
||||
@@ -152,7 +162,7 @@ final class LlmChainExtension extends Extension
|
||||
|
||||
if ('azure' === $type) {
|
||||
foreach ($platform as $name => $config) {
|
||||
$platformId = 'llm_chain.platform.azure.'.$name;
|
||||
$platformId = 'symfony_ai.platform.azure.'.$name;
|
||||
$definition = (new Definition(Platform::class))
|
||||
->setFactory(AzureOpenAIPlatformFactory::class.'::create')
|
||||
->setAutowired(true)
|
||||
@@ -164,7 +174,7 @@ final class LlmChainExtension extends Extension
|
||||
'$apiVersion' => $config['api_version'],
|
||||
'$apiKey' => $config['api_key'],
|
||||
])
|
||||
->addTag('llm_chain.platform');
|
||||
->addTag('symfony_ai.platform');
|
||||
|
||||
$container->setDefinition($platformId, $definition);
|
||||
}
|
||||
@@ -173,14 +183,14 @@ final class LlmChainExtension extends Extension
|
||||
}
|
||||
|
||||
if ('google' === $type) {
|
||||
$platformId = 'llm_chain.platform.google';
|
||||
$platformId = 'symfony_ai.platform.google';
|
||||
$definition = (new Definition(Platform::class))
|
||||
->setFactory(GooglePlatformFactory::class.'::create')
|
||||
->setAutowired(true)
|
||||
->setLazy(true)
|
||||
->addTag('proxy', ['interface' => PlatformInterface::class])
|
||||
->setArguments(['$apiKey' => $platform['api_key']])
|
||||
->addTag('llm_chain.platform');
|
||||
->addTag('symfony_ai.platform');
|
||||
|
||||
$container->setDefinition($platformId, $definition);
|
||||
|
||||
@@ -188,27 +198,27 @@ final class LlmChainExtension extends Extension
|
||||
}
|
||||
|
||||
if ('openai' === $type) {
|
||||
$platformId = 'llm_chain.platform.openai';
|
||||
$platformId = 'symfony_ai.platform.openai';
|
||||
$definition = (new Definition(Platform::class))
|
||||
->setFactory(OpenAIPlatformFactory::class.'::create')
|
||||
->setAutowired(true)
|
||||
->setLazy(true)
|
||||
->addTag('proxy', ['interface' => PlatformInterface::class])
|
||||
->setArguments(['$apiKey' => $platform['api_key']])
|
||||
->addTag('llm_chain.platform');
|
||||
->addTag('symfony_ai.platform');
|
||||
|
||||
$container->setDefinition($platformId, $definition);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('Platform "%s" is not supported for configuration via bundle at this point.', $type));
|
||||
throw new \InvalidArgumentException(\sprintf('Platform "%s" is not supported for configuration via bundle at this point.', $type));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $config
|
||||
*/
|
||||
private function processChainConfig(string $name, array $config, ContainerBuilder $container): void
|
||||
private function processAgentConfig(string $name, array $config, ContainerBuilder $container): void
|
||||
{
|
||||
// MODEL
|
||||
['name' => $modelName, 'version' => $version, 'options' => $options] = $config['model'];
|
||||
@@ -218,23 +228,23 @@ final class LlmChainExtension extends Extension
|
||||
'claude' => Claude::class,
|
||||
'llama' => Llama::class,
|
||||
'gemini' => Gemini::class,
|
||||
default => throw new \InvalidArgumentException(sprintf('Model "%s" is not supported.', $modelName)),
|
||||
default => throw new \InvalidArgumentException(\sprintf('Model "%s" is not supported.', $modelName)),
|
||||
};
|
||||
$modelDefinition = new Definition($modelClass);
|
||||
if (null !== $version) {
|
||||
$modelDefinition->setArgument('$name', $version);
|
||||
}
|
||||
if (0 !== count($options)) {
|
||||
if (0 !== \count($options)) {
|
||||
$modelDefinition->setArgument('$options', $options);
|
||||
}
|
||||
$modelDefinition->addTag('llm_chain.model.language_model');
|
||||
$container->setDefinition('llm_chain.chain.'.$name.'.model', $modelDefinition);
|
||||
$modelDefinition->addTag('symfony_ai.model.language_model');
|
||||
$container->setDefinition('symfony_ai.agent.'.$name.'.model', $modelDefinition);
|
||||
|
||||
// CHAIN
|
||||
$chainDefinition = (new Definition(Chain::class))
|
||||
// AGENT
|
||||
$agentDefinition = (new Definition(Agent::class))
|
||||
->setAutowired(true)
|
||||
->setArgument('$platform', new Reference($config['platform']))
|
||||
->setArgument('$model', new Reference('llm_chain.chain.'.$name.'.model'));
|
||||
->setArgument('$model', new Reference('symfony_ai.agent.'.$name.'.model'));
|
||||
|
||||
$inputProcessors = [];
|
||||
$outputProcessors = [];
|
||||
@@ -242,57 +252,57 @@ final class LlmChainExtension extends Extension
|
||||
// TOOL & PROCESSOR
|
||||
if ($config['tools']['enabled']) {
|
||||
// Create specific toolbox and process if tools are explicitly defined
|
||||
if (0 !== count($config['tools']['services'])) {
|
||||
if (0 !== \count($config['tools']['services'])) {
|
||||
$memoryFactoryDefinition = new Definition(MemoryToolFactory::class);
|
||||
$container->setDefinition('llm_chain.toolbox.'.$name.'.memory_factory', $memoryFactoryDefinition);
|
||||
$container->setDefinition('symfony_ai.toolbox.'.$name.'.memory_factory', $memoryFactoryDefinition);
|
||||
$chainFactoryDefinition = new Definition(ChainFactory::class, [
|
||||
'$factories' => [new Reference('llm_chain.toolbox.'.$name.'.memory_factory'), new Reference(ReflectionToolFactory::class)],
|
||||
'$factories' => [new Reference('symfony_ai.toolbox.'.$name.'.memory_factory'), new Reference(ReflectionToolFactory::class)],
|
||||
]);
|
||||
$container->setDefinition('llm_chain.toolbox.'.$name.'.chain_factory', $chainFactoryDefinition);
|
||||
$container->setDefinition('symfony_ai.toolbox.'.$name.'.chain_factory', $chainFactoryDefinition);
|
||||
|
||||
$tools = [];
|
||||
foreach ($config['tools']['services'] as $tool) {
|
||||
$reference = new Reference($tool['service']);
|
||||
// We use the memory factory in case method, description and name are set
|
||||
if (isset($tool['name'], $tool['description'])) {
|
||||
if ($tool['is_chain']) {
|
||||
$chainWrapperDefinition = new Definition(ChainTool::class, ['$chain' => $reference]);
|
||||
$container->setDefinition('llm_chain.toolbox.'.$name.'.chain_wrapper.'.$tool['name'], $chainWrapperDefinition);
|
||||
$reference = new Reference('llm_chain.toolbox.'.$name.'.chain_wrapper.'.$tool['name']);
|
||||
if ($tool['is_agent']) {
|
||||
$chainWrapperDefinition = new Definition(AgentTool::class, ['$agent' => $reference]);
|
||||
$container->setDefinition('symfony_ai.toolbox.'.$name.'.agent_wrapper.'.$tool['name'], $chainWrapperDefinition);
|
||||
$reference = new Reference('symfony_ai.toolbox.'.$name.'.agent_wrapper.'.$tool['name']);
|
||||
}
|
||||
$memoryFactoryDefinition->addMethodCall('addTool', [$reference, $tool['name'], $tool['description'], $tool['method'] ?? '__invoke']);
|
||||
}
|
||||
$tools[] = $reference;
|
||||
}
|
||||
|
||||
$toolboxDefinition = (new ChildDefinition('llm_chain.toolbox.abstract'))
|
||||
->replaceArgument('$toolFactory', new Reference('llm_chain.toolbox.'.$name.'.chain_factory'))
|
||||
$toolboxDefinition = (new ChildDefinition('symfony_ai.toolbox.abstract'))
|
||||
->replaceArgument('$toolFactory', new Reference('symfony_ai.toolbox.'.$name.'.chain_factory'))
|
||||
->replaceArgument('$tools', $tools);
|
||||
$container->setDefinition('llm_chain.toolbox.'.$name, $toolboxDefinition);
|
||||
$container->setDefinition('symfony_ai.toolbox.'.$name, $toolboxDefinition);
|
||||
|
||||
if ($config['fault_tolerant_toolbox']) {
|
||||
$faultTolerantToolboxDefinition = (new Definition('llm_chain.fault_tolerant_toolbox.'.$name))
|
||||
$faultTolerantToolboxDefinition = (new Definition('symfony_ai.fault_tolerant_toolbox.'.$name))
|
||||
->setClass(FaultTolerantToolbox::class)
|
||||
->setAutowired(true)
|
||||
->setDecoratedService('llm_chain.toolbox.'.$name);
|
||||
$container->setDefinition('llm_chain.fault_tolerant_toolbox.'.$name, $faultTolerantToolboxDefinition);
|
||||
->setDecoratedService('symfony_ai.toolbox.'.$name);
|
||||
$container->setDefinition('symfony_ai.fault_tolerant_toolbox.'.$name, $faultTolerantToolboxDefinition);
|
||||
}
|
||||
|
||||
if ($container->getParameter('kernel.debug')) {
|
||||
$traceableToolboxDefinition = (new Definition('llm_chain.traceable_toolbox.'.$name))
|
||||
$traceableToolboxDefinition = (new Definition('symfony_ai.traceable_toolbox.'.$name))
|
||||
->setClass(TraceableToolbox::class)
|
||||
->setAutowired(true)
|
||||
->setDecoratedService('llm_chain.toolbox.'.$name)
|
||||
->addTag('llm_chain.traceable_toolbox');
|
||||
$container->setDefinition('llm_chain.traceable_toolbox.'.$name, $traceableToolboxDefinition);
|
||||
->setDecoratedService('symfony_ai.toolbox.'.$name)
|
||||
->addTag('symfony_ai.traceable_toolbox');
|
||||
$container->setDefinition('symfony_ai.traceable_toolbox.'.$name, $traceableToolboxDefinition);
|
||||
}
|
||||
|
||||
$toolProcessorDefinition = (new ChildDefinition('llm_chain.tool.chain_processor.abstract'))
|
||||
->replaceArgument('$toolbox', new Reference('llm_chain.toolbox.'.$name));
|
||||
$container->setDefinition('llm_chain.tool.chain_processor.'.$name, $toolProcessorDefinition);
|
||||
$toolProcessorDefinition = (new ChildDefinition('symfony_ai.tool.agent_processor.abstract'))
|
||||
->replaceArgument('$toolbox', new Reference('symfony_ai.toolbox.'.$name));
|
||||
$container->setDefinition('symfony_ai.tool.agent_processor.'.$name, $toolProcessorDefinition);
|
||||
|
||||
$inputProcessors[] = new Reference('llm_chain.tool.chain_processor.'.$name);
|
||||
$outputProcessors[] = new Reference('llm_chain.tool.chain_processor.'.$name);
|
||||
$inputProcessors[] = new Reference('symfony_ai.tool.agent_processor.'.$name);
|
||||
$outputProcessors[] = new Reference('symfony_ai.tool.agent_processor.'.$name);
|
||||
} else {
|
||||
$inputProcessors[] = new Reference(ToolProcessor::class);
|
||||
$outputProcessors[] = new Reference(ToolProcessor::class);
|
||||
@@ -306,23 +316,23 @@ final class LlmChainExtension extends Extension
|
||||
}
|
||||
|
||||
// SYSTEM PROMPT
|
||||
if (is_string($config['system_prompt'])) {
|
||||
if (\is_string($config['system_prompt'])) {
|
||||
$systemPromptInputProcessorDefinition = new Definition(SystemPromptInputProcessor::class);
|
||||
$systemPromptInputProcessorDefinition
|
||||
->setAutowired(true)
|
||||
->setArguments([
|
||||
'$systemPrompt' => $config['system_prompt'],
|
||||
'$toolbox' => $config['include_tools'] ? new Reference('llm_chain.toolbox.'.$name) : null,
|
||||
'$toolbox' => $config['include_tools'] ? new Reference('symfony_ai.toolbox.'.$name) : null,
|
||||
]);
|
||||
|
||||
$inputProcessors[] = $systemPromptInputProcessorDefinition;
|
||||
}
|
||||
|
||||
$chainDefinition
|
||||
$agentDefinition
|
||||
->setArgument('$inputProcessors', $inputProcessors)
|
||||
->setArgument('$outputProcessors', $outputProcessors);
|
||||
|
||||
$container->setDefinition('llm_chain.chain.'.$name, $chainDefinition);
|
||||
$container->setDefinition('symfony_ai.agent.'.$name, $agentDefinition);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -339,17 +349,17 @@ final class LlmChainExtension extends Extension
|
||||
'$apiVersion' => $store['api_version'],
|
||||
];
|
||||
|
||||
if (array_key_exists('vector_field', $store)) {
|
||||
if (\array_key_exists('vector_field', $store)) {
|
||||
$arguments['$vectorFieldName'] = $store['vector_field'];
|
||||
}
|
||||
|
||||
$definition = new Definition(AzureSearchStore::class);
|
||||
$definition
|
||||
->setAutowired(true)
|
||||
->addTag('llm_chain.store')
|
||||
->addTag('symfony_ai.store')
|
||||
->setArguments($arguments);
|
||||
|
||||
$container->setDefinition('llm_chain.store.'.$type.'.'.$name, $definition);
|
||||
$container->setDefinition('symfony_ai.store.'.$type.'.'.$name, $definition);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,9 +369,9 @@ final class LlmChainExtension extends Extension
|
||||
$definition
|
||||
->setAutowired(true)
|
||||
->setArgument('$collectionName', $store['collection'])
|
||||
->addTag('llm_chain.store');
|
||||
->addTag('symfony_ai.store');
|
||||
|
||||
$container->setDefinition('llm_chain.store.'.$type.'.'.$name, $definition);
|
||||
$container->setDefinition('symfony_ai.store.'.$type.'.'.$name, $definition);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,21 +383,21 @@ final class LlmChainExtension extends Extension
|
||||
'$indexName' => $store['index_name'],
|
||||
];
|
||||
|
||||
if (array_key_exists('vector_field', $store)) {
|
||||
if (\array_key_exists('vector_field', $store)) {
|
||||
$arguments['$vectorFieldName'] = $store['vector_field'];
|
||||
}
|
||||
|
||||
if (array_key_exists('bulk_write', $store)) {
|
||||
if (\array_key_exists('bulk_write', $store)) {
|
||||
$arguments['$bulkWrite'] = $store['bulk_write'];
|
||||
}
|
||||
|
||||
$definition = new Definition(MongoDBStore::class);
|
||||
$definition
|
||||
->setAutowired(true)
|
||||
->addTag('llm_chain.store')
|
||||
->addTag('symfony_ai.store')
|
||||
->setArguments($arguments);
|
||||
|
||||
$container->setDefinition('llm_chain.store.'.$type.'.'.$name, $definition);
|
||||
$container->setDefinition('symfony_ai.store.'.$type.'.'.$name, $definition);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,21 +407,21 @@ final class LlmChainExtension extends Extension
|
||||
'$namespace' => $store['namespace'],
|
||||
];
|
||||
|
||||
if (array_key_exists('filter', $store)) {
|
||||
if (\array_key_exists('filter', $store)) {
|
||||
$arguments['$filter'] = $store['filter'];
|
||||
}
|
||||
|
||||
if (array_key_exists('top_k', $store)) {
|
||||
if (\array_key_exists('top_k', $store)) {
|
||||
$arguments['$topK'] = $store['top_k'];
|
||||
}
|
||||
|
||||
$definition = new Definition(PineconeStore::class);
|
||||
$definition
|
||||
->setAutowired(true)
|
||||
->addTag('llm_chain.store')
|
||||
->addTag('symfony_ai.store')
|
||||
->setArguments($arguments);
|
||||
|
||||
$container->setDefinition('llm_chain.store.'.$type.'.'.$name, $definition);
|
||||
$container->setDefinition('symfony_ai.store.'.$type.'.'.$name, $definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -426,24 +436,24 @@ final class LlmChainExtension extends Extension
|
||||
$modelClass = match (strtolower((string) $modelName)) {
|
||||
'embeddings' => Embeddings::class,
|
||||
'voyage' => Voyage::class,
|
||||
default => throw new \InvalidArgumentException(sprintf('Model "%s" is not supported.', $modelName)),
|
||||
default => throw new \InvalidArgumentException(\sprintf('Model "%s" is not supported.', $modelName)),
|
||||
};
|
||||
$modelDefinition = (new Definition($modelClass));
|
||||
if (null !== $version) {
|
||||
$modelDefinition->setArgument('$name', $version);
|
||||
}
|
||||
if (0 !== count($options)) {
|
||||
if (0 !== \count($options)) {
|
||||
$modelDefinition->setArgument('$options', $options);
|
||||
}
|
||||
$modelDefinition->addTag('llm_chain.model.embeddings_model');
|
||||
$container->setDefinition('llm_chain.embedder.'.$name.'.model', $modelDefinition);
|
||||
$modelDefinition->addTag('symfony_ai.model.embeddings_model');
|
||||
$container->setDefinition('symfony_ai.embedder.'.$name.'.model', $modelDefinition);
|
||||
|
||||
$definition = new Definition(Embedder::class, [
|
||||
'$model' => new Reference('llm_chain.embedder.'.$name.'.model'),
|
||||
'$model' => new Reference('symfony_ai.embedder.'.$name.'.model'),
|
||||
'$platform' => new Reference($config['platform']),
|
||||
'$store' => new Reference($config['store']),
|
||||
]);
|
||||
|
||||
$container->setDefinition('llm_chain.embedder.'.$name, $definition);
|
||||
$container->setDefinition('symfony_ai.embedder.'.$name, $definition);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* 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 PhpLlm\LlmChainBundle\DependencyInjection;
|
||||
namespace Symfony\AI\AIBundle\DependencyInjection;
|
||||
|
||||
use PhpLlm\LlmChain\Platform\PlatformInterface;
|
||||
use PhpLlm\LlmChain\Store\StoreInterface;
|
||||
use Symfony\AI\Platform\PlatformInterface;
|
||||
use Symfony\AI\Store\StoreInterface;
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
|
||||
/**
|
||||
* @author Christopher Hertel <mail@christopher-hertel.de>
|
||||
*/
|
||||
final class Configuration implements ConfigurationInterface
|
||||
{
|
||||
public function getConfigTreeBuilder(): TreeBuilder
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('llm_chain');
|
||||
$treeBuilder = new TreeBuilder('ai');
|
||||
$rootNode = $treeBuilder->getRootNode();
|
||||
|
||||
$rootNode
|
||||
@@ -50,7 +60,7 @@ final class Configuration implements ConfigurationInterface
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('chain')
|
||||
->arrayNode('agent')
|
||||
->normalizeKeys(false)
|
||||
->useAttributeAsKey('name')
|
||||
->arrayPrototype()
|
||||
@@ -75,7 +85,7 @@ final class Configuration implements ConfigurationInterface
|
||||
->thenInvalid('The default system prompt must not be an empty string')
|
||||
->end()
|
||||
->defaultNull()
|
||||
->info('The default system prompt of the chain')
|
||||
->info('The default system prompt of the agent')
|
||||
->end()
|
||||
->booleanNode('include_tools')
|
||||
->info('Include tool definitions at the end of the system prompt')
|
||||
@@ -104,7 +114,7 @@ final class Configuration implements ConfigurationInterface
|
||||
->scalarNode('name')->end()
|
||||
->scalarNode('description')->end()
|
||||
->scalarNode('method')->end()
|
||||
->booleanNode('is_chain')->defaultFalse()->end()
|
||||
->booleanNode('is_agent')->defaultFalse()->end()
|
||||
->end()
|
||||
->beforeNormalization()
|
||||
->ifString()
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpLlm\LlmChainBundle;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
final class LlmChainBundle extends Bundle
|
||||
{
|
||||
}
|
||||
@@ -1,17 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* 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 PhpLlm\LlmChainBundle\Profiler;
|
||||
namespace Symfony\AI\AIBundle\Profiler;
|
||||
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\ToolboxInterface;
|
||||
use PhpLlm\LlmChain\Platform\Tool\Tool;
|
||||
use Symfony\AI\Agent\Toolbox\ToolboxInterface;
|
||||
use Symfony\AI\Platform\Tool\Tool;
|
||||
use Symfony\Bundle\FrameworkBundle\DataCollector\AbstractDataCollector;
|
||||
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @author Christopher Hertel <mail@christopher-hertel.de>
|
||||
*
|
||||
* @phpstan-import-type PlatformCallData from TraceablePlatform
|
||||
* @phpstan-import-type ToolCallData from TraceableToolbox
|
||||
*/
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* 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 PhpLlm\LlmChainBundle\Profiler;
|
||||
namespace Symfony\AI\AIBundle\Profiler;
|
||||
|
||||
use PhpLlm\LlmChain\Platform\Message\Content\File;
|
||||
use PhpLlm\LlmChain\Platform\Model;
|
||||
use PhpLlm\LlmChain\Platform\PlatformInterface;
|
||||
use PhpLlm\LlmChain\Platform\Response\ResponseInterface;
|
||||
use Symfony\AI\Platform\Message\Content\File;
|
||||
use Symfony\AI\Platform\Model;
|
||||
use Symfony\AI\Platform\PlatformInterface;
|
||||
use Symfony\AI\Platform\Response\ResponseInterface;
|
||||
|
||||
/**
|
||||
* @author Christopher Hertel <mail@christopher-hertel.de>
|
||||
*
|
||||
* @phpstan-type PlatformCallData array{
|
||||
* model: Model,
|
||||
* input: array<mixed>|string|object,
|
||||
@@ -39,7 +48,7 @@ final class TraceablePlatform implements PlatformInterface
|
||||
|
||||
$this->calls[] = [
|
||||
'model' => $model,
|
||||
'input' => is_object($input) ? clone $input : $input,
|
||||
'input' => \is_object($input) ? clone $input : $input,
|
||||
'options' => $options,
|
||||
'response' => $response->getContent(),
|
||||
];
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* 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 PhpLlm\LlmChainBundle\Profiler;
|
||||
namespace Symfony\AI\AIBundle\Profiler;
|
||||
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\ToolboxInterface;
|
||||
use PhpLlm\LlmChain\Platform\Response\ToolCall;
|
||||
use Symfony\AI\Agent\Toolbox\ToolboxInterface;
|
||||
use Symfony\AI\Platform\Response\ToolCall;
|
||||
|
||||
/**
|
||||
* @author Christopher Hertel <mail@christopher-hertel.de>
|
||||
*
|
||||
* @phpstan-type ToolCallData array{
|
||||
* call: ToolCall,
|
||||
* result: string,
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* 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\Component\DependencyInjection\Loader\Configurator;
|
||||
|
||||
use PhpLlm\LlmChain\Chain\StructuredOutput\ChainProcessor as StructureOutputProcessor;
|
||||
use PhpLlm\LlmChain\Chain\StructuredOutput\ResponseFormatFactory;
|
||||
use PhpLlm\LlmChain\Chain\StructuredOutput\ResponseFormatFactoryInterface;
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\ChainProcessor as ToolProcessor;
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\Toolbox;
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\ToolboxInterface;
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\ToolFactory\ReflectionToolFactory;
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\ToolFactoryInterface;
|
||||
use PhpLlm\LlmChainBundle\Profiler\DataCollector;
|
||||
use PhpLlm\LlmChainBundle\Profiler\TraceableToolbox;
|
||||
use Symfony\AI\Agent\StructuredOutput\AgentProcessor as StructureOutputProcessor;
|
||||
use Symfony\AI\Agent\StructuredOutput\ResponseFormatFactory;
|
||||
use Symfony\AI\Agent\StructuredOutput\ResponseFormatFactoryInterface;
|
||||
use Symfony\AI\Agent\Toolbox\AgentProcessor as ToolProcessor;
|
||||
use Symfony\AI\Agent\Toolbox\Toolbox;
|
||||
use Symfony\AI\Agent\Toolbox\ToolboxInterface;
|
||||
use Symfony\AI\Agent\Toolbox\ToolFactory\ReflectionToolFactory;
|
||||
use Symfony\AI\Agent\Toolbox\ToolFactoryInterface;
|
||||
use Symfony\AI\AIBundle\Profiler\DataCollector;
|
||||
use Symfony\AI\AIBundle\Profiler\TraceableToolbox;
|
||||
|
||||
return static function (ContainerConfigurator $container): void {
|
||||
$container->services()
|
||||
@@ -24,11 +31,11 @@ return static function (ContainerConfigurator $container): void {
|
||||
->set(ResponseFormatFactory::class)
|
||||
->alias(ResponseFormatFactoryInterface::class, ResponseFormatFactory::class)
|
||||
->set(StructureOutputProcessor::class)
|
||||
->tag('llm_chain.chain.input_processor')
|
||||
->tag('llm_chain.chain.output_processor')
|
||||
->tag('symfony_ai.agent.input_processor')
|
||||
->tag('symfony_ai.agent.output_processor')
|
||||
|
||||
// tools
|
||||
->set('llm_chain.toolbox.abstract')
|
||||
->set('symfony_ai.toolbox.abstract')
|
||||
->class(Toolbox::class)
|
||||
->autowire()
|
||||
->abstract()
|
||||
@@ -37,23 +44,23 @@ return static function (ContainerConfigurator $container): void {
|
||||
'$tools' => abstract_arg('Collection of tools'),
|
||||
])
|
||||
->set(Toolbox::class)
|
||||
->parent('llm_chain.toolbox.abstract')
|
||||
->parent('symfony_ai.toolbox.abstract')
|
||||
->args([
|
||||
'$tools' => tagged_iterator('llm_chain.tool'),
|
||||
'$tools' => tagged_iterator('symfony_ai.tool'),
|
||||
])
|
||||
->alias(ToolboxInterface::class, Toolbox::class)
|
||||
->set(ReflectionToolFactory::class)
|
||||
->alias(ToolFactoryInterface::class, ReflectionToolFactory::class)
|
||||
->set('llm_chain.tool.chain_processor.abstract')
|
||||
->set('symfony_ai.tool.chain_processor.abstract')
|
||||
->class(ToolProcessor::class)
|
||||
->abstract()
|
||||
->args([
|
||||
'$toolbox' => abstract_arg('Toolbox'),
|
||||
])
|
||||
->set(ToolProcessor::class)
|
||||
->parent('llm_chain.tool.chain_processor.abstract')
|
||||
->tag('llm_chain.chain.input_processor')
|
||||
->tag('llm_chain.chain.output_processor')
|
||||
->parent('symfony_ai.tool.chain_processor.abstract')
|
||||
->tag('symfony_ai.agent.input_processor')
|
||||
->tag('symfony_ai.agent.output_processor')
|
||||
->args([
|
||||
'$toolbox' => service(ToolboxInterface::class),
|
||||
'$eventDispatcher' => service('event_dispatcher')->nullOnInvalid(),
|
||||
@@ -64,6 +71,6 @@ return static function (ContainerConfigurator $container): void {
|
||||
->tag('data_collector')
|
||||
->set(TraceableToolbox::class)
|
||||
->decorate(ToolboxInterface::class)
|
||||
->tag('llm_chain.traceable_toolbox')
|
||||
->tag('symfony_ai.traceable_toolbox')
|
||||
;
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{% block toolbar %}
|
||||
{% if collector.platformCalls|length > 0 %}
|
||||
{% set icon %}
|
||||
{{ include('@LlmChain/icon.svg', { y: 18 }) }}
|
||||
{{ include('@AI/icon.svg', { y: 18 }) }}
|
||||
<span class="sf-toolbar-value">{{ collector.platformCalls|length }}</span>
|
||||
<span class="sf-toolbar-info-piece-additional-detail">
|
||||
<span class="sf-toolbar-label">calls</span>
|
||||
@@ -37,8 +37,8 @@
|
||||
|
||||
{% block menu %}
|
||||
<span class="label">
|
||||
<span class="icon">{{ include('@LlmChain/icon.svg', { y: 16 }) }}</span>
|
||||
<strong>LLM Chain</strong>
|
||||
<span class="icon">{{ include('@AI/icon.svg', { y: 16 }) }}</span>
|
||||
<strong>Symfony AI</strong>
|
||||
<span class="count">{{ collector.platformCalls|length }}</span>
|
||||
</span>
|
||||
{% endblock %}
|
||||
@@ -56,7 +56,7 @@
|
||||
{% endmacro %}
|
||||
|
||||
{% block panel %}
|
||||
<h2>LLM Chain</h2>
|
||||
<h2>Symfony AI</h2>
|
||||
<section class="metrics">
|
||||
<div class="metric-group">
|
||||
<div class="metric">
|
||||
|
||||
@@ -12,5 +12,5 @@
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<text x="12" y="{{ y }}" font-family="Lilita One, Segoe UI Emoji" font-size="14px" fill="currentColor" text-anchor="middle">LLM</text>
|
||||
<text x="12" y="{{ y }}" font-family="Lilita One, Segoe UI Emoji" font-size="14px" fill="currentColor" text-anchor="middle">AI</text>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@@ -1,18 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* 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 PhpLlm\LlmChainBundle\Tests\Profiler;
|
||||
namespace Symfony\AI\AIBundle\Tests\Profiler;
|
||||
|
||||
use PhpLlm\LlmChain\Chain\Toolbox\ToolboxInterface;
|
||||
use PhpLlm\LlmChain\Platform\Response\ToolCall;
|
||||
use PhpLlm\LlmChain\Platform\Tool\ExecutionReference;
|
||||
use PhpLlm\LlmChain\Platform\Tool\Tool;
|
||||
use PhpLlm\LlmChainBundle\Profiler\TraceableToolbox;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Small;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\AI\Agent\Toolbox\ToolboxInterface;
|
||||
use Symfony\AI\AIBundle\Profiler\TraceableToolbox;
|
||||
use Symfony\AI\Platform\Response\ToolCall;
|
||||
use Symfony\AI\Platform\Tool\ExecutionReference;
|
||||
use Symfony\AI\Platform\Tool\Tool;
|
||||
|
||||
#[CoversClass(TraceableToolbox::class)]
|
||||
#[Small]
|
||||
|
||||
Reference in New Issue
Block a user