├── .readme.yaml
├── LICENSE
├── README.md
├── composer.json
├── config
├── chat_request_handler.php
├── commands.php
├── completion_request_handler.php
├── experts.php
├── image_request_handler.php
└── providers
│ ├── anthropic
│ ├── chat.php
│ └── common.php
│ ├── fireworksai
│ ├── chat.php
│ ├── common.php
│ ├── completion.php
│ ├── embeddings.php
│ └── image.php
│ ├── mistral
│ ├── chat.php
│ ├── common.php
│ └── embeddings.php
│ ├── ollama
│ ├── chat.php
│ ├── common.php
│ ├── completion.php
│ └── embeddings.php
│ └── openai
│ ├── chat.php
│ ├── common.php
│ ├── embeddings.php
│ └── image.php
└── src
├── Command
└── ChatCommand.php
├── Config
└── CriteriaContainer.php
├── Criteria
├── ModelCriteria.php
└── ProviderCriteria.php
├── DecisionTree
└── DecisionTreeDecorator.php
└── ModelflowAiBundle.php
/.readme.yaml:
--------------------------------------------------------------------------------
1 | title: Symfony Integration
2 | package: symfony-bundle
3 | description: This bundle integrates Modelflow-AI into a Symfony project.
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Johannes Wachter
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
5 |
6 |
7 | Modelflow AI
8 | Symfony Integration
9 |
10 |
11 |
12 |
13 |
14 |
15 | This bundle integrates Modelflow-AI into a Symfony project.
16 |
17 |
18 |
19 |
20 | > **Note**:
21 | > This is part of the `modelflow-ai` project create issues in the [main repository](https://github.com/modelflow-ai/.github).
22 |
23 | > **Note**:
24 | > This project is heavily under development and any feedback is greatly appreciated.
25 |
26 |
27 |
28 | ## Installation
29 |
30 | To install the Symfony Integration package, you need to have PHP 8.2 or higher and Composer installed on your machine.
31 | Then, you can add the package to your project by running the following command:
32 |
33 | ```bash
34 | composer require modelflow-ai/symfony-bundle
35 | ```
36 |
37 | ## Usage
38 |
39 | Detailed usage instructions will be provided in the future. For now, you can refer to the source code and unit tests for
40 | usage examples.
41 |
42 | ## Contributing
43 |
44 | Contributions are welcome. Please open an issue or submit a pull request in the main repository
45 | at [https://github.com/modelflow-ai/.github](https://github.com/modelflow-ai/.github).
46 |
47 | ## License
48 |
49 | This project is licensed under the MIT License. For the full copyright and license information, please view the LICENSE
50 | file that was distributed with this source code.
51 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "modelflow-ai/symfony-bundle",
3 | "description": "An integration of modelflow-ai via a bundle into the Symfony Framework.",
4 | "type": "library",
5 | "license": "MIT",
6 | "keywords": [
7 | "ai",
8 | "openai",
9 | "mistral",
10 | "ollama",
11 | "anthropic",
12 | "fireworksai",
13 | "gpt4",
14 | "gpt3.5",
15 | "llama2",
16 | "llama3.1",
17 | "mistral-large"
18 | ],
19 | "autoload": {
20 | "psr-4": {
21 | "ModelflowAi\\Integration\\Symfony\\": "src/"
22 | }
23 | },
24 | "autoload-dev": {
25 | "psr-4": {
26 | "ModelflowAi\\Integration\\Symfony\\Tests\\": "tests/"
27 | }
28 | },
29 | "authors": [
30 | {
31 | "name": "Johannes Wachter",
32 | "email": "johannes@sulu.io"
33 | }
34 | ],
35 | "require": {
36 | "php": "^8.2",
37 | "symfony/console": "^6.2 | ^7.0",
38 | "symfony/dependency-injection": "^6.2 | ^7.0",
39 | "symfony/http-kernel": "^6.2 | ^7.0",
40 | "symfony/config": "^6.2 | ^7.0",
41 | "symfony/framework-bundle": "^6.2 | ^7.0"
42 | },
43 | "require-dev": {
44 | "modelflow-ai/anthropic-adapter": "^0.2",
45 | "modelflow-ai/chat": "^0.2",
46 | "modelflow-ai/completion": "^0.2",
47 | "modelflow-ai/embeddings": "^0.2",
48 | "modelflow-ai/experts": "^0.2",
49 | "modelflow-ai/fireworksai-adapter": "^0.2",
50 | "modelflow-ai/image": "^0.2",
51 | "modelflow-ai/mistral-adapter": "^0.2",
52 | "modelflow-ai/ollama-adapter": "^0.2",
53 | "modelflow-ai/openai-adapter": "^0.2",
54 | "modelflow-ai/prompt-template": "^0.2",
55 | "php-cs-fixer/shim": "^3.15",
56 | "phpstan/extension-installer": "^1.2",
57 | "phpstan/phpstan": "^1.10, <1.10.55",
58 | "phpstan/phpstan-phpunit": "^1.3",
59 | "phpunit/phpunit": "^10.3",
60 | "rector/rector": "^0.18.1",
61 | "symfony/yaml": "^6.2 | ^7.0",
62 | "symfony/filesystem": "^6.2 | ^7.0",
63 | "asapo/remove-vendor-plugin": "^0.1"
64 | },
65 | "suggest": {
66 | "modelflow-ai/chat": "Library to handle chat requests.",
67 | "modelflow-ai/completion": "Library to handle completion requests.",
68 | "modelflow-ai/embeddings": "Library to manage embeddings.",
69 | "modelflow-ai/experts": "Library that provide experts.",
70 | "modelflow-ai/fireworksai-adapter": "Adapter to interact with fireworksai models.",
71 | "modelflow-ai/image": "Library to generate and manipulate images.",
72 | "modelflow-ai/anthropic-adapter": "Adapter to interact with anthropic models.",
73 | "modelflow-ai/mistral-adapter": "Adapter to interact with mistral models.",
74 | "modelflow-ai/ollama-adapter": "Adapter to interact with ollama models.",
75 | "modelflow-ai/openai-adapter": "Adapter to interact with openai models."
76 | },
77 | "scripts": {
78 | "test-with-coverage": "@test --coverage-php var/reports/coverage.cov --coverage-cobertura=var/cobertura-coverage.xml --coverage-html var/reports/html --log-junit var/reports/junit.xml",
79 | "test": [
80 | "Composer\\Config::disableProcessTimeout",
81 | "vendor/bin/phpunit"
82 | ],
83 | "phpstan": "@php vendor/bin/phpstan analyze",
84 | "lint-rector": "@php vendor/bin/rector process --dry-run",
85 | "lint-php-cs": "@php vendor/bin/php-cs-fixer fix --verbose --diff --dry-run",
86 | "lint": [
87 | "@phpstan",
88 | "@lint-php-cs",
89 | "@lint-rector",
90 | "@lint-composer"
91 | ],
92 | "lint-composer": "@composer validate --strict",
93 | "rector": "@php vendor/bin/rector process",
94 | "php-cs-fix": "@php vendor/bin/php-cs-fixer fix",
95 | "fix": [
96 | "@rector",
97 | "@php-cs-fix"
98 | ]
99 | },
100 | "repositories": [
101 | {
102 | "type": "path",
103 | "url": "./../../packages/*",
104 | "options": {
105 | "symlink": false
106 | }
107 | }
108 | ],
109 | "minimum-stability": "dev",
110 | "config": {
111 | "allow-plugins": {
112 | "phpstan/extension-installer": true,
113 | "php-http/discovery": true,
114 | "asapo/remove-vendor-plugin": true
115 | }
116 | },
117 | "extra": {
118 | "remove-folders": [
119 | "modelflow-ai/*/vendor"
120 | ]
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/config/chat_request_handler.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\Chat\AIChatRequestHandler;
17 | use ModelflowAi\Chat\AIChatRequestHandlerInterface;
18 | use ModelflowAi\Chat\ToolInfo\ToolExecutor;
19 | use ModelflowAi\Chat\ToolInfo\ToolExecutorInterface;
20 | use ModelflowAi\Integration\Symfony\DecisionTree\DecisionTreeDecorator;
21 | use ModelflowAi\Integration\Symfony\ModelflowAiBundle;
22 |
23 | /*
24 | * @internal
25 | */
26 | return static function (ContainerConfigurator $container) {
27 | $container->services()
28 | ->set('modelflow_ai.chat_request_handler.decision_tree', DecisionTreeDecorator::class)
29 | ->args([
30 | tagged_iterator(ModelflowAiBundle::TAG_CHAT_DECISION_TREE_RULE),
31 | ]);
32 |
33 | $container->services()
34 | ->set('modelflow_ai.chat_request_handler', AIChatRequestHandler::class)
35 | ->args([
36 | service('modelflow_ai.chat_request_handler.decision_tree'),
37 | ])
38 | ->alias(AIChatRequestHandlerInterface::class, 'modelflow_ai.chat_request_handler');
39 |
40 | $container->services()
41 | ->set('modelflow_ai.tool_executor', ToolExecutor::class)
42 | ->alias(ToolExecutorInterface::class, 'modelflow_ai.tool_executor');
43 | };
44 |
--------------------------------------------------------------------------------
/config/commands.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\Integration\Symfony\Command\ChatCommand;
17 |
18 | /*
19 | * @internal
20 | */
21 | return static function (ContainerConfigurator $container) {
22 | $container->services()
23 | ->set('modelflow_ai.command.chat', ChatCommand::class)
24 | ->args([
25 | service('modelflow_ai.chat_request_handler'),
26 | ])
27 | ->tag('console.command', ['command' => 'modelflow-ai:chat']);
28 | };
29 |
--------------------------------------------------------------------------------
/config/completion_request_handler.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\Completion\AICompletionRequestHandler;
17 | use ModelflowAi\Completion\AICompletionRequestHandlerInterface;
18 | use ModelflowAi\Integration\Symfony\DecisionTree\DecisionTreeDecorator;
19 | use ModelflowAi\Integration\Symfony\ModelflowAiBundle;
20 |
21 | /*
22 | * @internal
23 | */
24 | return static function (ContainerConfigurator $container) {
25 | $container->services()
26 | ->set('modelflow_ai.completion_request_handler.decision_tree', DecisionTreeDecorator::class)
27 | ->args([
28 | tagged_iterator(ModelflowAiBundle::TAG_COMPLETION_DECISION_TREE_RULE),
29 | ]);
30 |
31 | $container->services()
32 | ->set('modelflow_ai.completion_request_handler', AICompletionRequestHandler::class)
33 | ->args([
34 | service('modelflow_ai.completion_request_handler.decision_tree'),
35 | ])
36 | ->alias(AICompletionRequestHandlerInterface::class, 'modelflow_ai.completion_request_handler');
37 | };
38 |
--------------------------------------------------------------------------------
/config/experts.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\Experts\ThreadFactory;
17 | use ModelflowAi\Experts\ThreadFactoryInterface;
18 |
19 | /*
20 | * @internal
21 | */
22 | return static function (ContainerConfigurator $container) {
23 | $container->services()
24 | ->set('modelflow_ai.experts.thread_factory', ThreadFactory::class)
25 | ->args([
26 | service('modelflow_ai.chat_request_handler'),
27 | ])
28 | ->alias(ThreadFactoryInterface::class, 'modelflow_ai.experts.thread_factory');
29 | };
30 |
--------------------------------------------------------------------------------
/config/image_request_handler.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\DecisionTree\DecisionTreeInterface;
17 | use ModelflowAi\Image\AIImageRequestHandler;
18 | use ModelflowAi\Image\AIImageRequestHandlerInterface;
19 | use ModelflowAi\Image\Middleware\HandleMiddleware;
20 | use ModelflowAi\Integration\Symfony\DecisionTree\DecisionTreeDecorator;
21 | use ModelflowAi\Integration\Symfony\ModelflowAiBundle;
22 |
23 | /*
24 | * @internal
25 | */
26 | return static function (ContainerConfigurator $container) {
27 | $container->services()
28 | ->set('modelflow_ai.image_request_handler.decision_tree', DecisionTreeDecorator::class)
29 | ->args([
30 | tagged_iterator(ModelflowAiBundle::TAG_IMAGE_DECISION_TREE_RULE),
31 | ])
32 | ->alias(DecisionTreeInterface::class, 'modelflow_ai.chat_request_handler.decision_tree');
33 |
34 | $container->services()
35 | ->set('modelflow_ai.image_request_handler.middleware.handle', HandleMiddleware::class)
36 | ->args([
37 | service('modelflow_ai.image_request_handler.decision_tree'),
38 | ]);
39 |
40 | $container->services()
41 | ->set('modelflow_ai.image_request_handler', AIImageRequestHandler::class)
42 | ->args([
43 | service('modelflow_ai.image_request_handler.middleware.handle'),
44 | ])
45 | ->alias(AIImageRequestHandlerInterface::class, 'modelflow_ai.image_request_handler');
46 | };
47 |
--------------------------------------------------------------------------------
/config/providers/anthropic/chat.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\AnthropicAdapter\Chat\AnthropicChatAdapterFactory;
17 |
18 | /*
19 | * @internal
20 | */
21 | return static function (ContainerConfigurator $container) {
22 | $container->services()
23 | ->set('modelflow_ai.providers.anthropic.chat_adapter_factory', AnthropicChatAdapterFactory::class)
24 | ->args([
25 | service('modelflow_ai.providers.anthropic.client'),
26 | '%modelflow_ai.providers.anthropic.max_tokens%',
27 | ]);
28 | };
29 |
--------------------------------------------------------------------------------
/config/providers/anthropic/common.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\Anthropic\Anthropic;
17 | use ModelflowAi\Anthropic\ClientInterface;
18 | use ModelflowAi\Anthropic\Factory;
19 |
20 | /*
21 | * @internal
22 | */
23 | return static function (ContainerConfigurator $container) {
24 | $container->services()
25 | ->set('modelflow_ai.providers.anthropic.client_factory', Factory::class)
26 | ->factory([Anthropic::class, 'factory'])
27 | ->call('withApiKey', ['%modelflow_ai.providers.anthropic.credentials.api_key%']);
28 |
29 | $container->services()
30 | ->set('modelflow_ai.providers.anthropic.client', ClientInterface::class)
31 | ->factory([service('modelflow_ai.providers.anthropic.client_factory'), 'make']);
32 | };
33 |
--------------------------------------------------------------------------------
/config/providers/fireworksai/chat.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\FireworksAiAdapter\Chat\FireworksAiChatAdapterFactory;
17 |
18 | /*
19 | * @internal
20 | */
21 | return static function (ContainerConfigurator $container) {
22 | $container->services()
23 | ->set('modelflow_ai.providers.fireworksai.chat_adapter_factory', FireworksAiChatAdapterFactory::class)
24 | ->args([
25 | service('modelflow_ai.providers.fireworksai.client'),
26 | ]);
27 | };
28 |
--------------------------------------------------------------------------------
/config/providers/fireworksai/common.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\FireworksAiAdapter\ClientFactory;
17 | use OpenAI\Client;
18 |
19 | /*
20 | * @internal
21 | */
22 | return static function (ContainerConfigurator $container) {
23 | $container->services()
24 | ->set('modelflow_ai.providers.fireworksai.client_factory', ClientFactory::class)
25 | ->factory([ClientFactory::class, 'create'])
26 | ->call('withApiKey', ['%modelflow_ai.providers.fireworksai.credentials.api_key%']);
27 |
28 | $container->services()
29 | ->set('modelflow_ai.providers.fireworksai.client', Client::class)
30 | ->factory([service('modelflow_ai.providers.fireworksai.client_factory'), 'make']);
31 | };
32 |
--------------------------------------------------------------------------------
/config/providers/fireworksai/completion.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | /*
17 | * @internal
18 | */
19 |
20 | use ModelflowAi\FireworksAiAdapter\Completion\FireworksAiCompletionAdapterFactory;
21 |
22 | return static function (ContainerConfigurator $container) {
23 | $container->services()
24 | ->set('modelflow_ai.providers.fireworksai.completion_adapter_factory', FireworksAiCompletionAdapterFactory::class)
25 | ->args([
26 | service('modelflow_ai.providers.fireworksai.client'),
27 | '%modelflow_ai.providers.fireworksai.max_tokens%',
28 | ]);
29 | };
30 |
--------------------------------------------------------------------------------
/config/providers/fireworksai/embeddings.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\FireworksAiAdapter\Embeddings\FireworksAiEmbeddingsAdapterFactory;
17 |
18 | /*
19 | * @internal
20 | */
21 | return static function (ContainerConfigurator $container) {
22 | $container->services()
23 | ->set('modelflow_ai.providers.fireworksai.embedding_adapter_factory', FireworksAiEmbeddingsAdapterFactory::class)
24 | ->args([
25 | service('modelflow_ai.providers.fireworksai.client'),
26 | ]);
27 | };
28 |
--------------------------------------------------------------------------------
/config/providers/fireworksai/image.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\FireworksAiAdapter\Image\FireworksAiImageAdapterFactory;
17 |
18 | /*
19 | * @internal
20 | */
21 | return static function (ContainerConfigurator $container) {
22 | $container->services()
23 | ->set('modelflow_ai.providers.fireworksai.image_adapter_factory', FireworksAiImageAdapterFactory::class)
24 | ->args([
25 | service('http_client'),
26 | param('modelflow_ai.providers.fireworksai.credentials.api_key'),
27 | ]);
28 | };
29 |
--------------------------------------------------------------------------------
/config/providers/mistral/chat.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\MistralAdapter\Chat\MistralChatAdapterFactory;
17 |
18 | /*
19 | * @internal
20 | */
21 | return static function (ContainerConfigurator $container) {
22 | $container->services()
23 | ->set('modelflow_ai.providers.mistral.chat_adapter_factory', MistralChatAdapterFactory::class)
24 | ->args([
25 | service('modelflow_ai.providers.mistral.client'),
26 | ]);
27 | };
28 |
--------------------------------------------------------------------------------
/config/providers/mistral/common.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\Mistral\ClientInterface;
17 | use ModelflowAi\Mistral\Factory;
18 | use ModelflowAi\Mistral\Mistral;
19 |
20 | /*
21 | * @internal
22 | */
23 | return static function (ContainerConfigurator $container) {
24 | $container->services()
25 | ->set('modelflow_ai.providers.mistral.client_factory', Factory::class)
26 | ->factory([Mistral::class, 'factory'])
27 | ->call('withApiKey', ['%modelflow_ai.providers.mistral.credentials.api_key%']);
28 |
29 | $container->services()
30 | ->set('modelflow_ai.providers.mistral.client', ClientInterface::class)
31 | ->factory([service('modelflow_ai.providers.mistral.client_factory'), 'make']);
32 | };
33 |
--------------------------------------------------------------------------------
/config/providers/mistral/embeddings.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\MistralAdapter\Embeddings\MistralEmbeddingsAdapterFactory;
17 |
18 | /*
19 | * @internal
20 | */
21 | return static function (ContainerConfigurator $container) {
22 | $container->services()
23 | ->set('modelflow_ai.providers.mistral.embedding_adapter_factory', MistralEmbeddingsAdapterFactory::class)
24 | ->args([
25 | service('modelflow_ai.providers.mistral.client'),
26 | ]);
27 | };
28 |
--------------------------------------------------------------------------------
/config/providers/ollama/chat.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\OllamaAdapter\Chat\OllamaChatAdapterFactory;
17 |
18 | /*
19 | * @internal
20 | */
21 | return static function (ContainerConfigurator $container) {
22 | $container->services()
23 | ->set('modelflow_ai.providers.ollama.chat_adapter_factory', OllamaChatAdapterFactory::class)
24 | ->args([
25 | service('modelflow_ai.providers.ollama.client'),
26 | ]);
27 | };
28 |
--------------------------------------------------------------------------------
/config/providers/ollama/common.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\Ollama\ClientInterface;
17 | use ModelflowAi\Ollama\Factory;
18 | use ModelflowAi\Ollama\Ollama;
19 |
20 | /*
21 | * @internal
22 | */
23 | return static function (ContainerConfigurator $container) {
24 | $container->services()
25 | ->set('modelflow_ai.providers.ollama.client_factory', Factory::class)
26 | ->factory([Ollama::class, 'factory'])
27 | ->call('withBaseUrl', ['%modelflow_ai.providers.ollama.url%']);
28 |
29 | $container->services()
30 | ->set('modelflow_ai.providers.ollama.client', ClientInterface::class)
31 | ->factory([service('modelflow_ai.providers.ollama.client_factory'), 'make']);
32 | };
33 |
--------------------------------------------------------------------------------
/config/providers/ollama/completion.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\OllamaAdapter\Completion\OllamaCompletionAdapterFactory;
17 |
18 | /*
19 | * @internal
20 | */
21 | return static function (ContainerConfigurator $container) {
22 | $container->services()
23 | ->set('modelflow_ai.providers.ollama.completion_adapter_factory', OllamaCompletionAdapterFactory::class)
24 | ->args([
25 | service('modelflow_ai.providers.ollama.client'),
26 | ]);
27 | };
28 |
--------------------------------------------------------------------------------
/config/providers/ollama/embeddings.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\OllamaAdapter\Embeddings\OllamaEmbeddingsAdapterFactory;
17 |
18 | /*
19 | * @internal
20 | */
21 | return static function (ContainerConfigurator $container) {
22 | $container->services()
23 | ->set('modelflow_ai.providers.ollama.embedding_adapter_factory', OllamaEmbeddingsAdapterFactory::class)
24 | ->args([
25 | service('modelflow_ai.providers.ollama.client'),
26 | ]);
27 | };
28 |
--------------------------------------------------------------------------------
/config/providers/openai/chat.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\OpenaiAdapter\Chat\OpenaiChatAdapterFactory;
17 |
18 | /*
19 | * @internal
20 | */
21 | return static function (ContainerConfigurator $container) {
22 | $container->services()
23 | ->set('modelflow_ai.providers.openai.chat_adapter_factory', OpenaiChatAdapterFactory::class)
24 | ->args([
25 | service('modelflow_ai.providers.openai.client'),
26 | ]);
27 | };
28 |
--------------------------------------------------------------------------------
/config/providers/openai/common.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use OpenAI\Client;
17 | use OpenAI\Factory;
18 |
19 | /*
20 | * @internal
21 | */
22 | return static function (ContainerConfigurator $container) {
23 | $container->services()
24 | ->set('modelflow_ai.providers.openai.client_factory', Factory::class)
25 | ->factory([\OpenAI::class, 'factory'])
26 | ->call('withApiKey', ['%modelflow_ai.providers.openai.credentials.api_key%']);
27 |
28 | $container->services()
29 | ->set('modelflow_ai.providers.openai.client', Client::class)
30 | ->factory([service('modelflow_ai.providers.openai.client_factory'), 'make']);
31 | };
32 |
--------------------------------------------------------------------------------
/config/providers/openai/embeddings.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\OpenaiAdapter\Embeddings\OpenaiEmbeddingsAdapterFactory;
17 |
18 | /*
19 | * @internal
20 | */
21 | return static function (ContainerConfigurator $container) {
22 | $container->services()
23 | ->set('modelflow_ai.providers.openai.embedding_adapter_factory', OpenaiEmbeddingsAdapterFactory::class)
24 | ->args([
25 | service('modelflow_ai.providers.openai.client'),
26 | ]);
27 | };
28 |
--------------------------------------------------------------------------------
/config/providers/openai/image.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator;
15 |
16 | use ModelflowAi\OpenaiAdapter\Image\OpenaiImageAdapterFactory;
17 |
18 | /*
19 | * @internal
20 | */
21 | return static function (ContainerConfigurator $container) {
22 | $container->services()
23 | ->set('modelflow_ai.providers.openai.image_adapter_factory', OpenaiImageAdapterFactory::class)
24 | ->args([
25 | service('http_client'),
26 | service('modelflow_ai.providers.openai.client'),
27 | ]);
28 | };
29 |
--------------------------------------------------------------------------------
/src/Command/ChatCommand.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace ModelflowAi\Integration\Symfony\Command;
15 |
16 | use ModelflowAi\Chat\AIChatRequestHandlerInterface;
17 | use ModelflowAi\Chat\Request\Message\AIChatMessage;
18 | use ModelflowAi\Chat\Request\Message\AIChatMessageRoleEnum;
19 | use ModelflowAi\Chat\Response\AIChatResponse;
20 | use ModelflowAi\DecisionTree\Criteria\PrivacyCriteria;
21 | use ModelflowAi\PromptTemplate\ChatPromptTemplate;
22 | use Symfony\Component\Console\Command\Command;
23 | use Symfony\Component\Console\Input\InputInterface;
24 | use Symfony\Component\Console\Output\OutputInterface;
25 |
26 | final class ChatCommand extends Command
27 | {
28 | public function __construct(
29 | private readonly AIChatRequestHandlerInterface $requestHandler,
30 | ) {
31 | parent::__construct();
32 | }
33 |
34 | protected function execute(InputInterface $input, OutputInterface $output): int
35 | {
36 | /** @var AIChatResponse $response */
37 | $response = $this->requestHandler->createRequest(...ChatPromptTemplate::create(
38 | new AIChatMessage(AIChatMessageRoleEnum::SYSTEM, 'You are an {feeling} bot'),
39 | new AIChatMessage(AIChatMessageRoleEnum::USER, 'Hello {where}!'),
40 | )->format(['where' => 'world', 'feeling' => 'angry']))
41 | ->addCriteria(PrivacyCriteria::HIGH)
42 | ->build()
43 | ->execute();
44 |
45 | $output->writeln($response->getMessage()->content);
46 |
47 | return 0;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Config/CriteriaContainer.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace ModelflowAi\Integration\Symfony\Config;
15 |
16 | use ModelflowAi\DecisionTree\Criteria\CriteriaInterface;
17 | use ModelflowAi\DecisionTree\DecisionEnum;
18 |
19 | final readonly class CriteriaContainer implements CriteriaInterface, \Stringable
20 | {
21 | public function __construct(
22 | private CriteriaInterface $inner,
23 | ) {
24 | }
25 |
26 | public function matches(CriteriaInterface $toMatch): DecisionEnum
27 | {
28 | return $this->inner->matches($toMatch);
29 | }
30 |
31 | public function getValue(): int|string
32 | {
33 | return $this->inner->getValue();
34 | }
35 |
36 | public function getName(): string
37 | {
38 | return $this->inner->getName();
39 | }
40 |
41 | public function __toString(): string
42 | {
43 | return \sprintf(
44 | '!php/const %s::%s',
45 | $this->inner::class,
46 | $this->inner->getName(),
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Criteria/ModelCriteria.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace ModelflowAi\Integration\Symfony\Criteria;
15 |
16 | use ModelflowAi\DecisionTree\Criteria\CriteriaInterface;
17 | use ModelflowAi\DecisionTree\Criteria\FlagCriteriaTrait;
18 |
19 | enum ModelCriteria: string implements CriteriaInterface
20 | {
21 | use FlagCriteriaTrait;
22 |
23 | // Ollama
24 | case LLAMA2 = 'llama2';
25 | case LLAMA3 = 'llama3';
26 | case LLAMA3_2 = 'llama3.2';
27 | case NEXUSRAVEN = 'nexusraven';
28 | case LLAVA = 'llava';
29 |
30 | // OpenAI
31 | case GPT4O = 'gpt4o';
32 | case GPT4O_MINI = 'gpt4o-mini';
33 | case GPT4 = 'gpt4';
34 | case GPT3_5 = 'gpt3.5-turbo';
35 | case DALL_E_3 = 'dall-e-3';
36 | case DALL_E_2 = 'dall-e-2';
37 |
38 | // Mistral
39 | case MISTRAL_TINY = 'mistral-tiny';
40 | case MISTRAL_SMALL = 'mistral-small-latest';
41 | case MISTRAL_MEDIUM = 'mistral-medium-latest';
42 | case MISTRAL_LARGE = 'mistral-large-latest';
43 | case MISTRAL_NEMO = 'open-mistral-nemo';
44 |
45 | // Anthropic
46 | case CLAUDE_3_OPUS = 'claude-3-opus-20240229';
47 | case CLAUDE_3_5_SONNET = 'claude-3-5-sonnet-20241022';
48 | case CLAUDE_3_SONNET = 'claude-3-sonnet-20240229';
49 | case CLAUDE_3_5_HAIKU = 'claude-3-5-haiku-20241022';
50 | case CLAUDE_3_HAIKU = 'claude-3-haiku-20240307';
51 |
52 | // FireworksAI
53 | case LLAMA3_1_405B_FIREWORKS = 'accounts/fireworks/models/llama-v3p1-405b-instruct';
54 | case LLAMA3_1_70B_FIREWORKS = 'accounts/fireworks/models/llama-v3p1-70b-instruct';
55 | case LLAMA3_1_8B_FIREWORKS = 'accounts/fireworks/models/llama-v3p1-8b-instruct';
56 | case LLAMA3_70B_FIREWORKS = 'accounts/fireworks/models/llama-v3-70b-instruct';
57 | case FIREFUNCTION_V2_FIREWORKS = 'accounts/fireworks/models/firefunction-v2';
58 | case MIXTRAL_FIREWORKS = 'accounts/fireworks/models/mixtral-8x22b-instruct';
59 | case LLAVA_13B_FIREWORKS = 'accounts/fireworks/models/firellava-13b';
60 | case STABLE_DIFFUSSION_XL_1024_FIREWORKS = 'stable-diffusion-xl-1024-v1-0';
61 | }
62 |
--------------------------------------------------------------------------------
/src/Criteria/ProviderCriteria.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace ModelflowAi\Integration\Symfony\Criteria;
15 |
16 | use ModelflowAi\DecisionTree\Criteria\CriteriaInterface;
17 | use ModelflowAi\DecisionTree\Criteria\FlagCriteriaTrait;
18 |
19 | enum ProviderCriteria: string implements CriteriaInterface
20 | {
21 | use FlagCriteriaTrait;
22 |
23 | case OLLAMA = 'ollama';
24 | case OPENAI = 'openai';
25 | case MISTRAL = 'mistral';
26 | case ANTHROPIC = 'anthropic';
27 | case FIREWORKSAI = 'fireworksai';
28 | }
29 |
--------------------------------------------------------------------------------
/src/DecisionTree/DecisionTreeDecorator.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace ModelflowAi\Integration\Symfony\DecisionTree;
15 |
16 | use ModelflowAi\DecisionTree\Behaviour\CriteriaBehaviour;
17 | use ModelflowAi\DecisionTree\Behaviour\SupportsBehaviour;
18 | use ModelflowAi\DecisionTree\DecisionRuleInterface;
19 | use ModelflowAi\DecisionTree\DecisionTree;
20 | use ModelflowAi\DecisionTree\DecisionTreeInterface;
21 |
22 | /**
23 | * @template T of CriteriaBehaviour
24 | * @template U of SupportsBehaviour
25 | *
26 | * @implements DecisionTreeInterface
27 | */
28 | final readonly class DecisionTreeDecorator implements DecisionTreeInterface
29 | {
30 | /**
31 | * @var DecisionTreeInterface
32 | */
33 | private DecisionTreeInterface $inner;
34 |
35 | /**
36 | * @param \Traversable> $rules
37 | */
38 | public function __construct(
39 | \Traversable $rules,
40 | ) {
41 | $this->inner = new DecisionTree(\iterator_to_array($rules));
42 | }
43 |
44 | public function determineAdapter(object $request): object
45 | {
46 | return $this->inner->determineAdapter($request);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/ModelflowAiBundle.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace ModelflowAi\Integration\Symfony;
15 |
16 | use ModelflowAi\AnthropicAdapter\AnthropicAdapterPackage;
17 | use ModelflowAi\Chat\Adapter\AIChatAdapterInterface;
18 | use ModelflowAi\Chat\ChatPackage;
19 | use ModelflowAi\Completion\Adapter\AICompletionAdapterInterface;
20 | use ModelflowAi\Completion\CompletionPackage;
21 | use ModelflowAi\DecisionTree\Criteria\CapabilityCriteria;
22 | use ModelflowAi\DecisionTree\Criteria\CriteriaInterface;
23 | use ModelflowAi\DecisionTree\Criteria\FeatureCriteria;
24 | use ModelflowAi\DecisionTree\Criteria\PrivacyCriteria;
25 | use ModelflowAi\DecisionTree\DecisionRule;
26 | use ModelflowAi\Embeddings\Adapter\Cache\CacheEmbeddingAdapter;
27 | use ModelflowAi\Embeddings\Adapter\EmbeddingAdapterInterface;
28 | use ModelflowAi\Embeddings\EmbeddingsPackage;
29 | use ModelflowAi\Embeddings\Formatter\EmbeddingFormatter;
30 | use ModelflowAi\Embeddings\Generator\EmbeddingGenerator;
31 | use ModelflowAi\Embeddings\Splitter\EmbeddingSplitter;
32 | use ModelflowAi\Experts\Expert;
33 | use ModelflowAi\Experts\ResponseFormat\JsonSchemaResponseFormat;
34 | use ModelflowAi\Image\Adapter\AIImageAdapterInterface;
35 | use ModelflowAi\Image\ImagePackage;
36 | use ModelflowAi\Integration\Symfony\Config\CriteriaContainer;
37 | use ModelflowAi\Integration\Symfony\Criteria\ModelCriteria;
38 | use ModelflowAi\Integration\Symfony\Criteria\ProviderCriteria;
39 | use ModelflowAi\MistralAdapter\MistralAdapterPackage;
40 | use ModelflowAi\OllamaAdapter\OllamaAdapterPackage;
41 | use ModelflowAi\OpenaiAdapter\OpenAiAdapterPackage;
42 | use Symfony\Bundle\FrameworkBundle\Console\Application;
43 | use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
44 | use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
45 | use Symfony\Component\DependencyInjection\ContainerBuilder;
46 | use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
47 |
48 | use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
49 |
50 | use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
51 | use Symfony\Component\HttpKernel\KernelInterface;
52 |
53 | class ModelflowAiBundle extends AbstractBundle
54 | {
55 | final public const TAG_IMAGE_DECISION_TREE_RULE = 'modelflow_ai.image_request_handler.decision_tree.rule';
56 | final public const TAG_CHAT_DECISION_TREE_RULE = 'modelflow_ai.chat_request_handler.decision_tree.rule';
57 | final public const TAG_COMPLETION_DECISION_TREE_RULE = 'modelflow_ai.completion_request_handler.decision_tree.rule';
58 |
59 | protected string $extensionAlias = 'modelflow_ai';
60 |
61 | final public const DEFAULT_ADAPTER_KEY_ORDER = [
62 | 'enabled',
63 | 'model',
64 | 'provider',
65 | 'chat',
66 | 'completion',
67 | 'tools',
68 | 'image_to_text',
69 | 'text_to_image',
70 | 'criteria',
71 | 'priority',
72 | ];
73 |
74 | final public const DEFAULT_VALUES = [
75 | 'gpt4o_mini' => [
76 | 'provider' => ProviderCriteria::OPENAI->value,
77 | 'model' => ModelCriteria::GPT4O_MINI->value,
78 | 'chat' => true,
79 | 'completion' => false,
80 | 'stream' => true,
81 | 'tools' => true,
82 | 'image_to_text' => false,
83 | 'text_to_image' => false,
84 | 'criteria' => [
85 | ModelCriteria::GPT4O_MINI,
86 | ProviderCriteria::OPENAI,
87 | CapabilityCriteria::INTERMEDIATE,
88 | ],
89 | ],
90 | 'gpt4o' => [
91 | 'provider' => ProviderCriteria::OPENAI->value,
92 | 'model' => ModelCriteria::GPT4O->value,
93 | 'chat' => true,
94 | 'completion' => false,
95 | 'stream' => true,
96 | 'tools' => true,
97 | 'image_to_text' => false,
98 | 'text_to_image' => false,
99 | 'criteria' => [
100 | ModelCriteria::GPT4O,
101 | ProviderCriteria::OPENAI,
102 | CapabilityCriteria::SMART,
103 | ],
104 | ],
105 | 'gpt4' => [
106 | 'provider' => ProviderCriteria::OPENAI->value,
107 | 'model' => ModelCriteria::GPT4->value,
108 | 'chat' => true,
109 | 'completion' => false,
110 | 'stream' => true,
111 | 'tools' => true,
112 | 'image_to_text' => false,
113 | 'text_to_image' => false,
114 | 'criteria' => [
115 | ModelCriteria::GPT4,
116 | ProviderCriteria::OPENAI,
117 | CapabilityCriteria::SMART,
118 | ],
119 | ],
120 | 'gpt3.5' => [
121 | 'provider' => ProviderCriteria::OPENAI->value,
122 | 'model' => ModelCriteria::GPT3_5->value,
123 | 'chat' => true,
124 | 'completion' => false,
125 | 'stream' => true,
126 | 'tools' => true,
127 | 'image_to_text' => false,
128 | 'text_to_image' => false,
129 | 'criteria' => [
130 | ModelCriteria::GPT3_5,
131 | ProviderCriteria::OPENAI,
132 | CapabilityCriteria::INTERMEDIATE,
133 | ],
134 | ],
135 | 'dall_e_3' => [
136 | 'provider' => ProviderCriteria::OPENAI->value,
137 | 'model' => ModelCriteria::DALL_E_3->value,
138 | 'chat' => false,
139 | 'completion' => false,
140 | 'stream' => false,
141 | 'tools' => false,
142 | 'image_to_text' => false,
143 | 'text_to_image' => true,
144 | 'criteria' => [
145 | ModelCriteria::DALL_E_3,
146 | ProviderCriteria::OPENAI,
147 | CapabilityCriteria::BASIC,
148 | ],
149 | ],
150 | 'dall_e_2' => [
151 | 'provider' => ProviderCriteria::OPENAI->value,
152 | 'model' => ModelCriteria::DALL_E_2->value,
153 | 'chat' => false,
154 | 'completion' => false,
155 | 'stream' => false,
156 | 'tools' => false,
157 | 'image_to_text' => false,
158 | 'text_to_image' => true,
159 | 'criteria' => [
160 | ModelCriteria::DALL_E_2,
161 | ProviderCriteria::OPENAI,
162 | CapabilityCriteria::BASIC,
163 | ],
164 | ],
165 | 'mistral_tiny' => [
166 | 'provider' => ProviderCriteria::MISTRAL->value,
167 | 'model' => ModelCriteria::MISTRAL_TINY->value,
168 | 'chat' => true,
169 | 'completion' => false,
170 | 'stream' => true,
171 | 'tools' => false,
172 | 'image_to_text' => false,
173 | 'text_to_image' => false,
174 | 'criteria' => [
175 | ModelCriteria::MISTRAL_TINY,
176 | ProviderCriteria::MISTRAL,
177 | CapabilityCriteria::BASIC,
178 | ],
179 | ],
180 | 'mistral_small' => [
181 | 'provider' => ProviderCriteria::MISTRAL->value,
182 | 'model' => ModelCriteria::MISTRAL_SMALL->value,
183 | 'chat' => true,
184 | 'completion' => false,
185 | 'stream' => true,
186 | 'tools' => false,
187 | 'image_to_text' => false,
188 | 'text_to_image' => false,
189 | 'criteria' => [
190 | ModelCriteria::MISTRAL_SMALL,
191 | ProviderCriteria::MISTRAL,
192 | CapabilityCriteria::INTERMEDIATE,
193 | ],
194 | ],
195 | 'mistral_medium' => [
196 | 'provider' => ProviderCriteria::MISTRAL->value,
197 | 'model' => ModelCriteria::MISTRAL_MEDIUM->value,
198 | 'chat' => true,
199 | 'completion' => false,
200 | 'stream' => true,
201 | 'tools' => false,
202 | 'image_to_text' => false,
203 | 'text_to_image' => false,
204 | 'criteria' => [
205 | ModelCriteria::MISTRAL_MEDIUM,
206 | ProviderCriteria::MISTRAL,
207 | CapabilityCriteria::ADVANCED,
208 | ],
209 | ],
210 | 'mistral_nemo' => [
211 | 'provider' => ProviderCriteria::MISTRAL->value,
212 | 'model' => ModelCriteria::MISTRAL_NEMO->value,
213 | 'chat' => true,
214 | 'completion' => false,
215 | 'stream' => true,
216 | 'tools' => true,
217 | 'image_to_text' => false,
218 | 'text_to_image' => false,
219 | 'criteria' => [
220 | ModelCriteria::MISTRAL_NEMO,
221 | ProviderCriteria::MISTRAL,
222 | CapabilityCriteria::INTERMEDIATE,
223 | ],
224 | ],
225 | 'mistral_large' => [
226 | 'provider' => ProviderCriteria::MISTRAL->value,
227 | 'model' => ModelCriteria::MISTRAL_LARGE->value,
228 | 'chat' => true,
229 | 'completion' => false,
230 | 'stream' => true,
231 | 'tools' => true,
232 | 'image_to_text' => false,
233 | 'text_to_image' => false,
234 | 'criteria' => [
235 | ModelCriteria::MISTRAL_LARGE,
236 | ProviderCriteria::MISTRAL,
237 | CapabilityCriteria::SMART,
238 | ],
239 | ],
240 | 'llama2' => [
241 | 'provider' => ProviderCriteria::OLLAMA->value,
242 | 'model' => ModelCriteria::LLAMA2->value,
243 | 'chat' => true,
244 | 'completion' => true,
245 | 'stream' => true,
246 | 'tools' => false,
247 | 'image_to_text' => false,
248 | 'text_to_image' => false,
249 | 'criteria' => [
250 | ModelCriteria::LLAMA2,
251 | ProviderCriteria::OLLAMA,
252 | CapabilityCriteria::BASIC,
253 | ],
254 | ],
255 | 'llama3' => [
256 | 'provider' => ProviderCriteria::OLLAMA->value,
257 | 'model' => ModelCriteria::LLAMA3->value,
258 | 'chat' => true,
259 | 'completion' => true,
260 | 'stream' => true,
261 | 'tools' => false,
262 | 'image_to_text' => false,
263 | 'text_to_image' => false,
264 | 'criteria' => [
265 | ModelCriteria::LLAMA3,
266 | ProviderCriteria::OLLAMA,
267 | CapabilityCriteria::BASIC,
268 | ],
269 | ],
270 | 'llama3_2' => [
271 | 'provider' => ProviderCriteria::OLLAMA->value,
272 | 'model' => ModelCriteria::LLAMA3_2->value,
273 | 'chat' => true,
274 | 'completion' => true,
275 | 'stream' => true,
276 | 'tools' => false,
277 | 'image_to_text' => false,
278 | 'text_to_image' => false,
279 | 'criteria' => [
280 | ModelCriteria::LLAMA3_2,
281 | ProviderCriteria::OLLAMA,
282 | CapabilityCriteria::BASIC,
283 | ],
284 | ],
285 | 'nexusraven' => [
286 | 'provider' => ProviderCriteria::OLLAMA->value,
287 | 'model' => ModelCriteria::NEXUSRAVEN->value,
288 | 'chat' => true,
289 | 'completion' => true,
290 | 'stream' => true,
291 | 'tools' => false,
292 | 'image_to_text' => false,
293 | 'text_to_image' => false,
294 | 'criteria' => [
295 | ModelCriteria::NEXUSRAVEN,
296 | ProviderCriteria::OLLAMA,
297 | CapabilityCriteria::BASIC,
298 | ],
299 | ],
300 | 'llava' => [
301 | 'provider' => ProviderCriteria::OLLAMA->value,
302 | 'model' => ModelCriteria::LLAVA->value,
303 | 'chat' => true,
304 | 'completion' => true,
305 | 'stream' => true,
306 | 'tools' => false,
307 | 'image_to_text' => true,
308 | 'text_to_image' => false,
309 | 'criteria' => [
310 | ModelCriteria::LLAVA,
311 | ProviderCriteria::OLLAMA,
312 | CapabilityCriteria::BASIC,
313 | ],
314 | ],
315 | 'claude_3_5_sonnet' => [
316 | 'provider' => ProviderCriteria::ANTHROPIC->value,
317 | 'model' => ModelCriteria::CLAUDE_3_5_SONNET->value,
318 | 'chat' => true,
319 | 'completion' => false,
320 | 'stream' => true,
321 | 'tools' => false,
322 | 'image_to_text' => true,
323 | 'text_to_image' => false,
324 | 'criteria' => [
325 | ModelCriteria::CLAUDE_3_5_SONNET,
326 | ProviderCriteria::ANTHROPIC,
327 | CapabilityCriteria::SMART,
328 | ],
329 | ],
330 | 'claude_3_opus' => [
331 | 'provider' => ProviderCriteria::ANTHROPIC->value,
332 | 'model' => ModelCriteria::CLAUDE_3_OPUS->value,
333 | 'chat' => true,
334 | 'completion' => false,
335 | 'stream' => true,
336 | 'tools' => false,
337 | 'image_to_text' => true,
338 | 'text_to_image' => false,
339 | 'criteria' => [
340 | ModelCriteria::CLAUDE_3_OPUS,
341 | ProviderCriteria::ANTHROPIC,
342 | CapabilityCriteria::SMART,
343 | ],
344 | ],
345 | 'claude_3_sonnet' => [
346 | 'provider' => ProviderCriteria::ANTHROPIC->value,
347 | 'model' => ModelCriteria::CLAUDE_3_SONNET->value,
348 | 'chat' => true,
349 | 'completion' => false,
350 | 'stream' => true,
351 | 'tools' => false,
352 | 'image_to_text' => true,
353 | 'text_to_image' => false,
354 | 'criteria' => [
355 | ModelCriteria::CLAUDE_3_SONNET,
356 | ProviderCriteria::ANTHROPIC,
357 | CapabilityCriteria::ADVANCED,
358 | ],
359 | ],
360 | 'claude_3_5_haiku' => [
361 | 'provider' => ProviderCriteria::ANTHROPIC->value,
362 | 'model' => ModelCriteria::CLAUDE_3_5_HAIKU->value,
363 | 'chat' => true,
364 | 'completion' => false,
365 | 'stream' => true,
366 | 'tools' => false,
367 | 'image_to_text' => true,
368 | 'text_to_image' => false,
369 | 'criteria' => [
370 | ModelCriteria::CLAUDE_3_5_HAIKU,
371 | ProviderCriteria::ANTHROPIC,
372 | CapabilityCriteria::BASIC,
373 | ],
374 | ],
375 | 'claude_3_haiku' => [
376 | 'provider' => ProviderCriteria::ANTHROPIC->value,
377 | 'model' => ModelCriteria::CLAUDE_3_HAIKU->value,
378 | 'chat' => true,
379 | 'completion' => false,
380 | 'stream' => true,
381 | 'tools' => false,
382 | 'image_to_text' => true,
383 | 'text_to_image' => false,
384 | 'criteria' => [
385 | ModelCriteria::CLAUDE_3_HAIKU,
386 | ProviderCriteria::ANTHROPIC,
387 | CapabilityCriteria::BASIC,
388 | ],
389 | ],
390 | 'fireworksai_llama3_1_405b' => [
391 | 'provider' => ProviderCriteria::FIREWORKSAI->value,
392 | 'model' => ModelCriteria::LLAMA3_1_405B_FIREWORKS->value,
393 | 'chat' => true,
394 | 'completion' => true,
395 | 'stream' => true,
396 | 'tools' => false,
397 | 'image_to_text' => false,
398 | 'text_to_image' => false,
399 | 'criteria' => [
400 | ModelCriteria::LLAMA3_1_405B_FIREWORKS,
401 | ProviderCriteria::FIREWORKSAI,
402 | CapabilityCriteria::SMART,
403 | ],
404 | ],
405 | 'fireworksai_llama3_1_70b' => [
406 | 'provider' => ProviderCriteria::FIREWORKSAI->value,
407 | 'model' => ModelCriteria::LLAMA3_1_70B_FIREWORKS->value,
408 | 'chat' => true,
409 | 'completion' => true,
410 | 'stream' => true,
411 | 'tools' => false,
412 | 'image_to_text' => false,
413 | 'text_to_image' => false,
414 | 'criteria' => [
415 | ModelCriteria::LLAMA3_1_70B_FIREWORKS,
416 | ProviderCriteria::FIREWORKSAI,
417 | CapabilityCriteria::INTERMEDIATE,
418 | ],
419 | ],
420 | 'fireworksai_llama3_1_8b' => [
421 | 'provider' => ProviderCriteria::FIREWORKSAI->value,
422 | 'model' => ModelCriteria::LLAMA3_1_8B_FIREWORKS->value,
423 | 'chat' => true,
424 | 'completion' => true,
425 | 'stream' => true,
426 | 'tools' => false,
427 | 'image_to_text' => false,
428 | 'text_to_image' => false,
429 | 'criteria' => [
430 | ModelCriteria::LLAMA3_1_8B_FIREWORKS,
431 | ProviderCriteria::FIREWORKSAI,
432 | CapabilityCriteria::INTERMEDIATE,
433 | ],
434 | ],
435 | 'fireworksai_llama3_70b' => [
436 | 'provider' => ProviderCriteria::FIREWORKSAI->value,
437 | 'model' => ModelCriteria::LLAMA3_70B_FIREWORKS->value,
438 | 'chat' => true,
439 | 'completion' => true,
440 | 'stream' => true,
441 | 'tools' => false,
442 | 'image_to_text' => false,
443 | 'text_to_image' => false,
444 | 'criteria' => [
445 | ModelCriteria::LLAMA3_70B_FIREWORKS,
446 | ProviderCriteria::FIREWORKSAI,
447 | CapabilityCriteria::INTERMEDIATE,
448 | ],
449 | ],
450 | 'fireworksai_mixtral' => [
451 | 'provider' => ProviderCriteria::FIREWORKSAI->value,
452 | 'model' => ModelCriteria::MIXTRAL_FIREWORKS->value,
453 | 'chat' => true,
454 | 'completion' => true,
455 | 'stream' => true,
456 | 'tools' => false,
457 | 'image_to_text' => false,
458 | 'text_to_image' => false,
459 | 'criteria' => [
460 | ModelCriteria::MIXTRAL_FIREWORKS,
461 | ProviderCriteria::FIREWORKSAI,
462 | CapabilityCriteria::ADVANCED,
463 | ],
464 | ],
465 | 'fireworksai_firefunction_v2' => [
466 | 'provider' => ProviderCriteria::FIREWORKSAI->value,
467 | 'model' => ModelCriteria::FIREFUNCTION_V2_FIREWORKS->value,
468 | 'chat' => true,
469 | 'completion' => true,
470 | 'stream' => true,
471 | 'tools' => true,
472 | 'image_to_text' => false,
473 | 'text_to_image' => false,
474 | 'criteria' => [
475 | ModelCriteria::FIREFUNCTION_V2_FIREWORKS,
476 | ProviderCriteria::FIREWORKSAI,
477 | CapabilityCriteria::INTERMEDIATE,
478 | ],
479 | ],
480 | 'fireworksai_llava_13b' => [
481 | 'provider' => ProviderCriteria::FIREWORKSAI->value,
482 | 'model' => ModelCriteria::LLAVA_13B_FIREWORKS->value,
483 | 'chat' => true,
484 | 'completion' => true,
485 | 'stream' => true,
486 | 'tools' => false,
487 | 'image_to_text' => true,
488 | 'text_to_image' => false,
489 | 'criteria' => [
490 | ModelCriteria::LLAVA_13B_FIREWORKS,
491 | ProviderCriteria::FIREWORKSAI,
492 | CapabilityCriteria::BASIC,
493 | ],
494 | ],
495 | 'stable_diffusion_xl_fireworks' => [
496 | 'provider' => ProviderCriteria::FIREWORKSAI->value,
497 | 'model' => ModelCriteria::STABLE_DIFFUSSION_XL_1024_FIREWORKS->value,
498 | 'chat' => false,
499 | 'completion' => false,
500 | 'stream' => false,
501 | 'tools' => false,
502 | 'image_to_text' => false,
503 | 'text_to_image' => true,
504 | 'criteria' => [
505 | ModelCriteria::STABLE_DIFFUSSION_XL_1024_FIREWORKS,
506 | ProviderCriteria::FIREWORKSAI,
507 | CapabilityCriteria::BASIC,
508 | ],
509 | ],
510 | ];
511 |
512 | private function getCriteria(CriteriaInterface $criteria, bool $isReferenceDumping): CriteriaInterface
513 | {
514 | if ($isReferenceDumping) {
515 | return new CriteriaContainer($criteria);
516 | }
517 |
518 | return $criteria;
519 | }
520 |
521 | /**
522 | * @param CriteriaInterface[] $default
523 | */
524 | public function createCriteriaNode(array $default, bool $isReferenceDumping): ArrayNodeDefinition
525 | {
526 | $nodeDefinition = new ArrayNodeDefinition('criteria');
527 | $nodeDefinition
528 | ->defaultValue(\array_map(
529 | fn (CriteriaInterface $criteria) => $this->getCriteria(PrivacyCriteria::LOW, $isReferenceDumping),
530 | $default,
531 | ));
532 | $nodeDefinition
533 | ->beforeNormalization()
534 | ->ifArray()
535 | ->then(function ($value) use ($isReferenceDumping): array {
536 | $result = [];
537 | foreach ($value as $item) {
538 | if ($item instanceof CriteriaInterface) {
539 | $result[] = $this->getCriteria($item, $isReferenceDumping);
540 | } else {
541 | $result[] = $item;
542 | }
543 | }
544 |
545 | return $result;
546 | })
547 | ->end();
548 | $nodeDefinition
549 | ->variablePrototype()
550 | ->validate()
551 | ->ifTrue(static fn ($value): bool => !$value instanceof CriteriaInterface)
552 | ->thenInvalid('The value has to be an instance of CriteriaInterface')
553 | ->end()
554 | ->end();
555 |
556 | return $nodeDefinition;
557 | }
558 |
559 | public function configure(DefinitionConfigurator $definition): void
560 | {
561 | // @phpstan-ignore-next-line
562 | $arguments = $argv ?? $_SERVER['argv'] ?? null;
563 |
564 | $isReferenceDumping = false;
565 | $container = $this->container ?? null;
566 | if ($container && $arguments) {
567 | /** @var KernelInterface $kernel */
568 | $kernel = $container->get('kernel');
569 | $application = new Application($kernel);
570 | $command = $application->find($arguments[1] ?? null);
571 | $isReferenceDumping = 'config:dump-reference' === $command->getName();
572 | }
573 |
574 | $adapters = [];
575 |
576 | // @phpstan-ignore-next-line
577 | $definition->rootNode()
578 | ->children()
579 | ->arrayNode('providers')
580 | ->children()
581 | ->arrayNode('openai')
582 | ->children()
583 | ->booleanNode('enabled')->defaultFalse()->end()
584 | ->arrayNode('credentials')
585 | ->isRequired()
586 | ->children()
587 | ->scalarNode('api_key')->isRequired()->end()
588 | ->end()
589 | ->end()
590 | ->append($this->createCriteriaNode([PrivacyCriteria::LOW], $isReferenceDumping))
591 | ->end()
592 | ->end()
593 | ->arrayNode('mistral')
594 | ->children()
595 | ->booleanNode('enabled')->defaultFalse()->end()
596 | ->arrayNode('credentials')
597 | ->isRequired()
598 | ->children()
599 | ->scalarNode('api_key')->isRequired()->end()
600 | ->end()
601 | ->end()
602 | ->append($this->createCriteriaNode([PrivacyCriteria::MEDIUM], $isReferenceDumping))
603 | ->end()
604 | ->end()
605 | ->arrayNode('anthropic')
606 | ->children()
607 | ->booleanNode('enabled')->defaultFalse()->end()
608 | ->arrayNode('credentials')
609 | ->isRequired()
610 | ->children()
611 | ->scalarNode('api_key')->isRequired()->end()
612 | ->end()
613 | ->end()
614 | ->integerNode('max_tokens')->defaultValue(1024)->end()
615 | ->append($this->createCriteriaNode([PrivacyCriteria::LOW], $isReferenceDumping))
616 | ->end()
617 | ->end()
618 | ->arrayNode('fireworksai')
619 | ->children()
620 | ->booleanNode('enabled')->defaultFalse()->end()
621 | ->arrayNode('credentials')
622 | ->isRequired()
623 | ->children()
624 | ->scalarNode('api_key')->isRequired()->end()
625 | ->end()
626 | ->end()
627 | ->integerNode('max_tokens')->defaultValue(1024)->end()
628 | ->append($this->createCriteriaNode([PrivacyCriteria::LOW], $isReferenceDumping))
629 | ->end()
630 | ->end()
631 | ->arrayNode('ollama')
632 | ->children()
633 | ->booleanNode('enabled')->defaultFalse()->end()
634 | ->scalarNode('url')
635 | ->defaultValue('http://localhost:11434/api/')
636 | ->validate()
637 | ->ifTrue(static fn ($value): bool => !\filter_var($value, \FILTER_VALIDATE_URL))
638 | ->thenInvalid('The value has to be a valid URL')
639 | ->end()
640 | ->beforeNormalization()
641 | ->ifString()
642 | ->then(static fn ($value): string => \rtrim((string) $value, '/') . '/')
643 | ->end()
644 | ->end()
645 | ->append($this->createCriteriaNode([PrivacyCriteria::HIGH], $isReferenceDumping))
646 | ->end()
647 | ->end()
648 | ->arrayNode('custom')
649 | ->arrayPrototype()
650 | ->children()
651 | ->scalarNode('chat_factory')->end()
652 | ->scalarNode('completion_factory')->end()
653 | ->scalarNode('image_factory')->end()
654 | ->append($this->createCriteriaNode([], $isReferenceDumping))
655 | ->end()
656 | ->end()
657 | ->end()
658 | ->end()
659 | ->end()
660 | ->arrayNode('adapters')
661 | ->defaultValue([])
662 | ->info('You can configure your own adapter here or use a preconfigured one (see examples) and enable it.')
663 | ->example(self::DEFAULT_VALUES)
664 | ->beforeNormalization()
665 | ->ifArray()
666 | ->then(static function ($value) use (&$adapters): array {
667 | foreach ($value as $key => $item) {
668 | $value[$key]['key'] = $key;
669 | $adapters[$key] = $item;
670 | }
671 |
672 | return $value;
673 | })
674 | ->end()
675 | ->arrayPrototype()
676 | ->beforeNormalization()
677 | ->ifArray()
678 | ->then(static function ($value): array {
679 | $key = $value['key'];
680 | unset($value['key']);
681 |
682 | $explicitlyDisabled = ($value['enabled'] ?? null) === false;
683 | $enabled = $value['enabled'] ?? false;
684 | unset($value['enabled']);
685 |
686 | if (!$explicitlyDisabled && 0 !== \count($value)) {
687 | $enabled = true;
688 | }
689 |
690 | $value = \array_merge(self::DEFAULT_VALUES[$key] ?? [], $value);
691 | $value['enabled'] = $enabled;
692 |
693 | \uksort($value, fn ($key1, $key2) => (\array_search($key1, self::DEFAULT_ADAPTER_KEY_ORDER, true) > \array_search($key2, self::DEFAULT_ADAPTER_KEY_ORDER, true)) ? 1 : -1);
694 |
695 | return $value;
696 | })
697 | ->end()
698 | ->children()
699 | ->booleanNode('enabled')->defaultFalse()->end()
700 | ->scalarNode('provider')->isRequired()->end()
701 | ->scalarNode('model')->isRequired()->end()
702 | ->integerNode('priority')->defaultValue(0)->end()
703 | ->booleanNode('chat')->defaultFalse()->end()
704 | ->booleanNode('completion')->defaultFalse()->end()
705 | ->booleanNode('stream')->defaultFalse()->end()
706 | ->booleanNode('tools')->defaultFalse()->end()
707 | ->booleanNode('image_to_text')->defaultFalse()->end()
708 | ->booleanNode('text_to_image')->defaultFalse()->end()
709 | ->append($this->createCriteriaNode([], $isReferenceDumping))
710 | ->end()
711 | ->end()
712 | ->end()
713 | ->arrayNode('embeddings')
714 | ->children()
715 | ->arrayNode('generators')
716 | ->defaultValue([])
717 | ->arrayPrototype()
718 | ->children()
719 | ->booleanNode('enabled')->defaultFalse()->end()
720 | ->scalarNode('provider')->end()
721 | ->scalarNode('model')->end()
722 | ->arrayNode('splitter')
723 | ->addDefaultsIfNotSet()
724 | ->children()
725 | ->integerNode('max_length')->defaultValue(1000)->end()
726 | ->scalarNode('separator')->defaultValue(' ')->end()
727 | ->end()
728 | ->end()
729 | ->arrayNode('cache')
730 | ->children()
731 | ->booleanNode('enabled')->defaultFalse()->end()
732 | ->scalarNode('cache_pool')->end()
733 | ->end()
734 | ->end()
735 | ->end()
736 | ->end()
737 | ->end()
738 | ->end()
739 | ->end()
740 | ->arrayNode('experts')
741 | ->defaultValue([])
742 | ->info('You can configure your experts here.')
743 | ->arrayPrototype()
744 | ->children()
745 | ->scalarNode('name')->isRequired()->end()
746 | ->scalarNode('description')->isRequired()->end()
747 | ->scalarNode('instructions')->isRequired()->end()
748 | ->arrayNode('response_format')
749 | ->children()
750 | ->enumNode('type')->values(['json_schema'])->isRequired()->end()
751 | ->variableNode('schema')->end()
752 | ->end()
753 | ->end()
754 | ->append($this->createCriteriaNode([], $isReferenceDumping))
755 | ->end()
756 | ->end()
757 | ->end()
758 | ->end();
759 | }
760 |
761 | /**
762 | * @param array $array
763 | *
764 | * @return array
765 | */
766 | private function flattenArray(array $array, string $prefix = ''): array
767 | {
768 | $result = [];
769 | foreach ($array as $key => $value) {
770 | if (\is_array($value)) {
771 | $result = \array_merge($result, $this->flattenArray($value, $prefix . $key . '.'));
772 | } else {
773 | $result[$prefix . $key] = $value;
774 | }
775 | }
776 |
777 | return $result;
778 | }
779 |
780 | /**
781 | * @param array{
782 | * providers?: array{
783 | * openai: array{
784 | * enabled: bool,
785 | * credentials: array{
786 | * api_key: string
787 | * },
788 | * criteria: CriteriaInterface[]
789 | * },
790 | * mistral: array{
791 | * enabled: bool,
792 | * credentials: array{
793 | * api_key: string
794 | * },
795 | * criteria: CriteriaInterface[]
796 | * },
797 | * anthropic: array{
798 | * enabled: bool,
799 | * credentials: array{
800 | * api_key: string
801 | * },
802 | * max_tokens: int,
803 | * criteria: CriteriaInterface[]
804 | * },
805 | * fireworksai: array{
806 | * enabled: bool,
807 | * credentials: array{
808 | * api_key: string
809 | * },
810 | * max_tokens: int,
811 | * criteria: CriteriaInterface[]
812 | * },
813 | * ollama: array{
814 | * enabled: bool,
815 | * url: string,
816 | * criteria: CriteriaInterface[]
817 | * },
818 | * custom?: array
824 | * },
825 | * adapters?: array,
838 | * embeddings?: array{
839 | * generators: array
852 | * },
853 | * experts?: array,
863 | * } $config
864 | */
865 | public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
866 | {
867 | $providerConfig = $this->flattenArray($config['providers'] ?? []);
868 | foreach ($providerConfig as $key => $value) {
869 | $container->parameters()
870 | ->set('modelflow_ai.providers.' . $key, $value);
871 | }
872 |
873 | $container->import(\dirname(__DIR__) . '/config/commands.php');
874 |
875 | $providers = $config['providers'] ?? [];
876 | $customProviders = $providers['custom'] ?? [];
877 | unset($providers['custom']);
878 |
879 | $adapters = \array_filter($config['adapters'] ?? [], fn (array $adapter) => $adapter['enabled']);
880 | $providers = \array_filter($providers, fn (array $provider) => $provider['enabled']);
881 |
882 | $generators = $config['embeddings']['generators'] ?? [];
883 |
884 | $chatAdapters = [];
885 | $completionAdapters = [];
886 | $imageAdapters = [];
887 | $configFiles = [];
888 | foreach ($adapters as $key => $adapter) {
889 | $tmpConfigFiles[] = $adapter['provider'] . '/common.php';
890 | $isCustomProvider = \array_key_exists($adapter['provider'], $customProviders);
891 |
892 | if ($adapter['chat']) {
893 | $chatAdapters[] = $key;
894 | $tmpConfigFiles[] = $adapter['provider'] . '/chat.php';
895 | }
896 |
897 | if ($adapter['completion']) {
898 | $completionAdapters[] = $key;
899 | $tmpConfigFiles[] = $adapter['provider'] . '/completion.php';
900 | }
901 |
902 | if ($adapter['text_to_image']) {
903 | $imageAdapters[] = $key;
904 | $tmpConfigFiles[] = $adapter['provider'] . '/image.php';
905 | }
906 |
907 | if (!$isCustomProvider) {
908 | $configFiles = \array_merge($configFiles, $tmpConfigFiles);
909 | }
910 | }
911 |
912 | foreach ($generators as $generator) {
913 | $configFiles[] = $generator['provider'] . '/embeddings.php';
914 | }
915 |
916 | if ([] !== $chatAdapters && !\class_exists(ChatPackage::class)) {
917 | $chatAdapters = [];
918 | }
919 |
920 | if ([] !== $completionAdapters && !\class_exists(CompletionPackage::class)) {
921 | $completionAdapters = [];
922 | }
923 |
924 | if ([] !== $imageAdapters && !\class_exists(ImagePackage::class)) {
925 | $imageAdapters = [];
926 | }
927 |
928 | if (\count($generators) > 0 && !\class_exists(EmbeddingsPackage::class)) {
929 | throw new \Exception('Embeddings package is enabled but the package is not installed. Please install it with composer require modelflow-ai/embeddings');
930 | }
931 |
932 | $container->parameters()
933 | ->set('modelflow_ai.adapters', $adapters)
934 | ->set('modelflow_ai.providers', $providers);
935 |
936 | if (($providers['openai']['enabled'] ?? false) && !\class_exists(OpenAiAdapterPackage::class)) {
937 | throw new \Exception('OpenAi adapter is enabled but the OpenAi adapter library is not installed. Please install it with composer require modelflow-ai/openai-adapter');
938 | }
939 |
940 | if (($providers['mistral']['enabled'] ?? false) && !\class_exists(MistralAdapterPackage::class)) {
941 | throw new \Exception('Mistral adapter is enabled but the Mistral adapter library is not installed. Please install it with composer require modelflow-ai/mistral-adapter');
942 | }
943 |
944 | if (($providers['anthropic']['enabled'] ?? false) && !\class_exists(AnthropicAdapterPackage::class)) {
945 | throw new \Exception('Anthropic adapter is enabled but the Anthropic adapter library is not installed. Please install it with composer require modelflow-ai/anthropic-adapter');
946 | }
947 |
948 | if (($providers['ollama']['enabled'] ?? false) && !\class_exists(OllamaAdapterPackage::class)) {
949 | throw new \Exception('Ollama adapter is enabled but the Ollama adapter library is not installed. Please install it with composer require modelflow-ai/ollama-adapter');
950 | }
951 |
952 | foreach (\array_unique($configFiles) as $configFile) {
953 | $container->import(\dirname(__DIR__) . '/config/providers/' . $configFile);
954 | }
955 |
956 | $container->import(\dirname(__DIR__) . '/config/chat_request_handler.php');
957 |
958 | foreach ($chatAdapters as $key) {
959 | $adapter = $adapters[$key] ?? null;
960 | if (!$adapter) {
961 | throw new \Exception('Chat adapter ' . $key . ' is enabled but not configured.');
962 | }
963 |
964 | $provider = $providers[$adapter['provider']] ?? $customProviders[$adapter['provider']] ?? null;
965 | if (!$provider) {
966 | throw new \Exception('Chat adapter ' . $key . ' is enabled but the provider ' . $adapter['provider'] . ' is not enabled.');
967 | }
968 |
969 | $factory = $customProviders[$adapter['provider']]['chat_factory']
970 | ?? 'modelflow_ai.providers.' . $adapter['provider'] . '.chat_adapter_factory';
971 |
972 | $container->services()
973 | ->set('modelflow_ai.chat_adapter.' . $key . '.adapter', AIChatAdapterInterface::class)
974 | ->factory([service($factory), 'createChatAdapter'])
975 | ->args([
976 | $adapter,
977 | ]);
978 |
979 | $featureCriteria = [];
980 | if ($adapter['image_to_text']) {
981 | $featureCriteria[] = FeatureCriteria::IMAGE_TO_TEXT;
982 | }
983 | if ($adapter['tools']) {
984 | $featureCriteria[] = FeatureCriteria::TOOLS;
985 | }
986 | if ($adapter['stream']) {
987 | $featureCriteria[] = FeatureCriteria::STREAM;
988 | }
989 |
990 | $container->services()
991 | ->set('modelflow_ai.chat_adapter.' . $key . '.rule', DecisionRule::class)
992 | ->args([
993 | service('modelflow_ai.chat_adapter.' . $key . '.adapter'),
994 | \array_merge($provider['criteria'], $adapter['criteria'], $featureCriteria),
995 | ])
996 | ->tag(self::TAG_CHAT_DECISION_TREE_RULE);
997 | }
998 |
999 | $container->import(\dirname(__DIR__) . '/config/completion_request_handler.php');
1000 |
1001 | foreach ($completionAdapters as $key) {
1002 | $adapter = $adapters[$key] ?? null;
1003 | if (!$adapter) {
1004 | throw new \Exception('Completion adapter ' . $key . ' is enabled but not configured.');
1005 | }
1006 |
1007 | $provider = $providers[$adapter['provider']] ?? null;
1008 | if (!$provider) {
1009 | throw new \Exception('Completion adapter ' . $key . ' is enabled but the provider ' . $adapter['provider'] . ' is not enabled.');
1010 | }
1011 |
1012 | $factory = $customProviders[$adapter['provider']]['completion_factory']
1013 | ?? 'modelflow_ai.providers.' . $adapter['provider'] . '.completion_adapter_factory';
1014 |
1015 | $container->services()
1016 | ->set('modelflow_ai.completion_adapter.' . $key . '.adapter', AICompletionAdapterInterface::class)
1017 | ->factory([service($factory), 'createCompletionAdapter'])
1018 | ->args([
1019 | $adapter,
1020 | ]);
1021 |
1022 | $container->services()
1023 | ->set('modelflow_ai.completion_adapter.' . $key . '.rule', DecisionRule::class)
1024 | ->args([
1025 | service('modelflow_ai.completion_adapter.' . $key . '.adapter'),
1026 | \array_merge($provider['criteria'], $adapter['criteria']),
1027 | ])
1028 | ->tag(self::TAG_COMPLETION_DECISION_TREE_RULE);
1029 | }
1030 |
1031 | if ([] !== $imageAdapters && !\class_exists(ImagePackage::class)) {
1032 | throw new \Exception(
1033 | 'Image adapters are enabled but the image package is not installed. Please install it with composer require modelflow-ai/image',
1034 | );
1035 | }
1036 |
1037 | $container->import(\dirname(__DIR__) . '/config/image_request_handler.php');
1038 |
1039 | foreach ($imageAdapters as $key) {
1040 | $adapter = $adapters[$key] ?? null;
1041 | if (!$adapter) {
1042 | throw new \Exception('Image adapter ' . $key . ' is enabled but not configured.');
1043 | }
1044 |
1045 | $provider = $providers[$adapter['provider']] ?? null;
1046 | if (!$provider) {
1047 | throw new \Exception('Image adapter ' . $key . ' is enabled but the provider ' . $adapter['provider'] . ' is not enabled.');
1048 | }
1049 |
1050 | $factory = $customProviders[$adapter['provider']]['image_factory']
1051 | ?? 'modelflow_ai.providers.' . $adapter['provider'] . '.image_adapter_factory';
1052 |
1053 | $container->services()
1054 | ->set('modelflow_ai.image_adapter.' . $key . '.adapter', AIImageAdapterInterface::class)
1055 | ->factory([service($factory), 'createImageAdapter'])
1056 | ->args([
1057 | $adapter,
1058 | ]);
1059 |
1060 | $container->services()
1061 | ->set('modelflow_ai.image_adapter.' . $key . '.rule', DecisionRule::class)
1062 | ->args([
1063 | service('modelflow_ai.image_adapter.' . $key . '.adapter'),
1064 | \array_merge($provider['criteria'], $adapter['criteria']),
1065 | ])
1066 | ->tag(self::TAG_IMAGE_DECISION_TREE_RULE);
1067 | }
1068 |
1069 | foreach ($generators as $key => $embedding) {
1070 | $adapterId = $key . '.adapter';
1071 | $container->services()
1072 | ->set($adapterId, EmbeddingAdapterInterface::class)
1073 | ->factory([service('modelflow_ai.providers.' . $embedding['provider'] . '.embedding_adapter_factory'), 'createEmbeddingGenerator'])
1074 | ->args([
1075 | $embedding,
1076 | ]);
1077 |
1078 | if ($embedding['cache']['enabled']) {
1079 | $container->services()
1080 | ->set($adapterId . '.cache', CacheEmbeddingAdapter::class)
1081 | ->args([
1082 | service($adapterId),
1083 | service($embedding['cache']['cache_pool']),
1084 | ]);
1085 |
1086 | $adapterId .= '.cache';
1087 | }
1088 |
1089 | $container->services()
1090 | ->set($key . '.splitter', EmbeddingSplitter::class)
1091 | ->args([
1092 | $embedding['splitter']['max_length'],
1093 | $embedding['splitter']['separator'],
1094 | ]);
1095 |
1096 | $container->services()
1097 | ->set($key . '.formatter', EmbeddingFormatter::class);
1098 |
1099 | $container->services()
1100 | ->set($key . '.generator', EmbeddingGenerator::class)
1101 | ->args([
1102 | service($key . '.splitter'),
1103 | service($key . '.formatter'),
1104 | service($adapterId),
1105 | ]);
1106 | }
1107 |
1108 | $experts = $config['experts'] ?? [];
1109 | if (\count($experts) > 0) {
1110 | if (!\class_exists(Expert::class)) {
1111 | throw new \Exception('Experts package is enabled but the package is not installed. Please install it with composer require modelflow-ai/experts');
1112 | }
1113 |
1114 | $container->import(\dirname(__DIR__) . '/config/experts.php');
1115 | }
1116 |
1117 | foreach ($experts as $key => $expert) {
1118 | $responseFormatService = null;
1119 | $responseFormat = $expert['response_format'] ?? null;
1120 | if (null !== $responseFormat && 'json_schema' === $responseFormat['type']) {
1121 | $responseFormatId = 'modelflow_ai.experts.' . $key . '.response_format';
1122 | $responseFormatService = service($responseFormatId);
1123 | $container->services()
1124 | ->set($responseFormatId, JsonSchemaResponseFormat::class)
1125 | ->args([
1126 | $responseFormat['schema'],
1127 | ]);
1128 | }
1129 |
1130 | $container->services()
1131 | ->set('modelflow_ai.experts.' . $key, Expert::class)
1132 | ->args([
1133 | $expert['name'],
1134 | $expert['description'],
1135 | $expert['instructions'],
1136 | $expert['criteria'],
1137 | $responseFormatService,
1138 | ]);
1139 | }
1140 | }
1141 | }
1142 |
--------------------------------------------------------------------------------