diff --git a/composer.json b/composer.json index c4a44b8a..0431da85 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "An example application using Symfony Framework", "type": "project", "license": "MIT", - "minimum-stability": "stable", + "minimum-stability": "dev", "prefer-stable": true, "require": { "php": "^8.4", @@ -24,6 +24,7 @@ "dragonmantank/cron-expression": "^3.4", "lcobucci/jwt": "^5.5", "nelmio/api-doc-bundle": "^4.38", + "symfony/ai-bundle": "dev-main", "symfony/amqp-messenger": "7.3.*", "symfony/asset": "7.3.*", "symfony/console": "7.3.*", diff --git a/composer.lock b/composer.lock index 2de18b6a..59f796b1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6444a349f65bc1a7b0db7a81931116f9", + "content-hash": "f33516de4ade7fa4f34a4bd3ad657888", "packages": [ { "name": "doctrine/collections", @@ -1663,6 +1663,57 @@ }, "time": "2025-08-13T20:13:15+00:00" }, + { + "name": "oskarstark/enum-helper", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/OskarStark/enum-helper.git", + "reference": "89245d0966f394f643cb5b35e1dfcda30a41063f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/OskarStark/enum-helper/zipball/89245d0966f394f643cb5b35e1dfcda30a41063f", + "reference": "89245d0966f394f643cb5b35e1dfcda30a41063f", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "conflict": { + "phpunit/phpunit": "<10" + }, + "require-dev": { + "ergebnis/php-cs-fixer-config": "^5.16", + "phpstan/phpstan": "^1.11.8", + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "OskarStark\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oskar Stark", + "email": "oskarstark@googlemail.com" + } + ], + "description": "This library provides helpers for several enum operations", + "keywords": [ + "enum" + ], + "support": { + "issues": "https://github.com/OskarStark/enum-helper/issues", + "source": "https://github.com/OskarStark/enum-helper/tree/1.7.0" + }, + "time": "2025-07-30T08:08:45+00:00" + }, { "name": "phpdocumentor/reflection-common", "version": "2.2.0", @@ -2135,6 +2186,414 @@ }, "time": "2024-09-11T13:17:53+00:00" }, + { + "name": "symfony/ai-agent", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/symfony/ai-agent.git", + "reference": "23e1eb8fbfa58e924fd698101843543f8bfd9a5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/ai-agent/zipball/23e1eb8fbfa58e924fd698101843543f8bfd9a5c", + "reference": "23e1eb8fbfa58e924fd698101843543f8bfd9a5c", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": ">=8.2", + "phpdocumentor/reflection-docblock": "^5.4", + "phpstan/phpdoc-parser": "^2.1", + "psr/log": "^3.0", + "symfony/ai-platform": "@dev", + "symfony/clock": "^7.3|^8.0", + "symfony/http-client": "^7.3|^8.0", + "symfony/property-access": "^7.3|^8.0", + "symfony/property-info": "^7.3|^8.0", + "symfony/serializer": "^7.3|^8.0", + "symfony/type-info": "^7.3|^8.0" + }, + "require-dev": { + "mrmysql/youtube-transcript": "^0.0.5", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^11.5.13", + "symfony/ai-store": "@dev", + "symfony/cache": "^7.3|^8.0", + "symfony/css-selector": "^7.3|^8.0", + "symfony/dom-crawler": "^7.3|^8.0", + "symfony/event-dispatcher": "^7.3|^8.0", + "symfony/http-foundation": "^7.3|^8.0", + "symfony/translation": "^7.3|^8.0", + "symfony/translation-contracts": "^3.6" + }, + "default-branch": true, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/ai", + "name": "symfony/ai" + } + }, + "autoload": { + "psr-4": { + "Symfony\\AI\\Agent\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christopher Hertel", + "email": "mail@christopher-hertel.de" + }, + { + "name": "Oskar Stark", + "email": "oskarstark@googlemail.com" + } + ], + "description": "PHP library for building agentic applications.", + "keywords": [ + "Agent", + "ai", + "llm" + ], + "support": { + "source": "https://github.com/symfony/ai-agent/tree/main" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-09-28T20:58:03+00:00" + }, + { + "name": "symfony/ai-bundle", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/symfony/ai-bundle.git", + "reference": "3faf4b713564fe013a61d07bd07c90b3fe48c8bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/ai-bundle/zipball/3faf4b713564fe013a61d07bd07c90b3fe48c8bd", + "reference": "3faf4b713564fe013a61d07bd07c90b3fe48c8bd", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/ai-agent": "@dev", + "symfony/ai-platform": "@dev", + "symfony/ai-store": "@dev", + "symfony/config": "^7.3|^8.0", + "symfony/console": "^7.3|^8.0", + "symfony/dependency-injection": "^7.3|^8.0", + "symfony/framework-bundle": "^7.3|^8.0", + "symfony/string": "^7.3|^8.0" + }, + "require-dev": { + "google/auth": "^1.47", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^11.5", + "symfony/expression-language": "^7.3|^8.0", + "symfony/security-core": "^7.3|^8.0", + "symfony/translation": "^7.3|^8.0" + }, + "default-branch": true, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\AI\\AiBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christopher Hertel", + "email": "mail@christopher-hertel.de" + }, + { + "name": "Oskar Stark", + "email": "oskarstark@googlemail.com" + } + ], + "description": "Integration bundle for Symfony AI components", + "support": { + "source": "https://github.com/symfony/ai-bundle/tree/main" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-09-30T08:34:07+00:00" + }, + { + "name": "symfony/ai-platform", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/symfony/ai-platform.git", + "reference": "702ae787e3f63d65aed904fa330e5881a58e0b1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/ai-platform/zipball/702ae787e3f63d65aed904fa330e5881a58e0b1a", + "reference": "702ae787e3f63d65aed904fa330e5881a58e0b1a", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "oskarstark/enum-helper": "^1.5", + "php": ">=8.2", + "phpdocumentor/reflection-docblock": "^5.4", + "phpstan/phpdoc-parser": "^2.1", + "psr/log": "^3.0", + "symfony/clock": "^7.3|^8.0", + "symfony/http-client": "^7.3|^8.0", + "symfony/property-access": "^7.3|^8.0", + "symfony/property-info": "^7.3|^8.0", + "symfony/serializer": "^7.3|^8.0", + "symfony/type-info": "^7.3|^8.0", + "symfony/uid": "^7.3|^8.0" + }, + "require-dev": { + "async-aws/bedrock-runtime": "^0.1.0", + "codewithkyrian/transformers": "^0.6.2", + "google/auth": "^1.47", + "phpstan/phpstan": "^2.1.17", + "phpstan/phpstan-strict-rules": "^2.0", + "phpstan/phpstan-symfony": "^2.0.6", + "phpunit/phpunit": "^11.5", + "symfony/ai-agent": "@dev", + "symfony/console": "^7.3|^8.0", + "symfony/dotenv": "^7.3|^8.0", + "symfony/event-dispatcher": "^7.3|^8.0", + "symfony/finder": "^7.3|^8.0", + "symfony/process": "^7.3|^8.0", + "symfony/var-dumper": "^7.3|^8.0" + }, + "default-branch": true, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/ai", + "name": "symfony/ai" + } + }, + "autoload": { + "psr-4": { + "Symfony\\AI\\Platform\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christopher Hertel", + "email": "mail@christopher-hertel.de" + }, + { + "name": "Oskar Stark", + "email": "oskarstark@googlemail.com" + } + ], + "description": "PHP library for interacting with AI platform provider.", + "keywords": [ + "Gemini", + "OpenRouter", + "ai", + "aimlapi", + "albert", + "anthropic", + "azure", + "bedrock", + "cerebras", + "dockermodelrunner", + "elevenlabs", + "huggingface", + "inference", + "llama", + "lmstudio", + "meta", + "mistral", + "nova", + "ollama", + "openai", + "perplexity", + "replicate", + "transformers", + "vertexai", + "voyage" + ], + "support": { + "source": "https://github.com/symfony/ai-platform/tree/main" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-09-29T11:55:27+00:00" + }, + { + "name": "symfony/ai-store", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/symfony/ai-store.git", + "reference": "6f7e12b5d1923df221b8ecca04f315b4d0774c5f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/ai-store/zipball/6f7e12b5d1923df221b8ecca04f315b4d0774c5f", + "reference": "6f7e12b5d1923df221b8ecca04f315b4d0774c5f", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": ">=8.2", + "psr/log": "^3.0", + "symfony/ai-platform": "@dev", + "symfony/clock": "^7.3|^8.0", + "symfony/http-client": "^7.3|^8.0", + "symfony/uid": "^7.3|^8.0" + }, + "conflict": { + "mongodb/mongodb": "<1.21" + }, + "require-dev": { + "codewithkyrian/chromadb-php": "^0.2.1 || ^0.3 || ^0.4", + "doctrine/dbal": "^3.3 || ^4.0", + "mongodb/mongodb": "^1.21 || ^2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^11.5", + "probots-io/pinecone-php": "^1.0", + "symfony/cache": "^6.4 || ^7.1", + "symfony/console": "^6.4 || ^7.1", + "symfony/dependency-injection": "^6.4 || ^7.1", + "symfony/dom-crawler": "^6.4 || ^7.1" + }, + "default-branch": true, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/ai", + "name": "symfony/ai" + } + }, + "autoload": { + "psr-4": { + "Symfony\\AI\\Store\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christopher Hertel", + "email": "mail@christopher-hertel.de" + }, + { + "name": "Oskar Stark", + "email": "oskarstark@googlemail.com" + } + ], + "description": "Low-level abstraction for storing and retrieving documents in a vector store.", + "keywords": [ + "ai", + "azure", + "chromadb", + "clickhouse", + "cloudflare", + "mariadb", + "meilisearch", + "milvus", + "mongodb", + "neo4j", + "pinecone", + "postgres", + "qdrant", + "surrealdb", + "typesense", + "weaviate" + ], + "support": { + "source": "https://github.com/symfony/ai-store/tree/main" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-09-28T20:58:03+00:00" + }, { "name": "symfony/amqp-messenger", "version": "v7.3.2", @@ -11300,8 +11759,10 @@ } ], "aliases": [], - "minimum-stability": "stable", - "stability-flags": {}, + "minimum-stability": "dev", + "stability-flags": { + "symfony/ai-bundle": 20 + }, "prefer-stable": true, "prefer-lowest": false, "platform": { diff --git a/config/bundles.php b/config/bundles.php index 7196ad55..b37d3996 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -1,7 +1,5 @@ ['all' => true], Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], @@ -14,4 +12,5 @@ Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], + Symfony\AI\AiBundle\AiBundle::class => ['all' => true], ]; diff --git a/config/packages/ai.php b/config/packages/ai.php new file mode 100644 index 00000000..2770e70d --- /dev/null +++ b/config/packages/ai.php @@ -0,0 +1,34 @@ +services()->defaults()->autowire()->autoconfigure(); + $services->set(Wikipedia::class); + + // --- Platforms --- + $ai->platform()->gemini()->apiKey(value: "%env(GEMINI_API_KEY)%"); + + // --- Agents --- + $agent = $ai->agent(name: "wikipedia"); + $model = $agent->model([ + "name" => "gemini-2.5-flash", + "options" => [ + "temperature" => 0.5, + ], + ]); + $agent->tools([ + "enabled" => true, + "services" => [ + Wikipedia::class + ], + ]); + $model->prompt([ + "text" => "Please answer the users question based on Wikipedia and provide a link to the article.", + "include_tools" => true, + ]); +}; diff --git a/src/Application/MessageHandler/AskAiChat/AskAiChatHandler.php b/src/Application/MessageHandler/AskAiChat/AskAiChatHandler.php new file mode 100644 index 00000000..cb4e2493 --- /dev/null +++ b/src/Application/MessageHandler/AskAiChat/AskAiChatHandler.php @@ -0,0 +1,45 @@ +sessionKey; + if (empty($sessionKey)) { + $sessionKey = Uuid::v7()->toString(); + } + + $messages = new MessageBag(); + + $messages->add(Message::ofUser($request->message)); + $result = $this->agent->call($messages); + + assert($result instanceof TextResult); + + $message = (string) $result->getContent(); + $messages->add(Message::ofAssistant($message)); + + return AskAiChatResult::success($message); + } +} diff --git a/src/Application/MessageHandler/AskAiChat/AskAiChatRequest.php b/src/Application/MessageHandler/AskAiChat/AskAiChatRequest.php new file mode 100644 index 00000000..f356e50e --- /dev/null +++ b/src/Application/MessageHandler/AskAiChat/AskAiChatRequest.php @@ -0,0 +1,21 @@ + $message, + ], + status: Response::HTTP_OK, + ); + } +} diff --git a/src/Presentation/Controller/Chat/AskController.php b/src/Presentation/Controller/Chat/AskController.php new file mode 100644 index 00000000..a420da83 --- /dev/null +++ b/src/Presentation/Controller/Chat/AskController.php @@ -0,0 +1,28 @@ +getHandledResult($request); + } +} diff --git a/symfony.lock b/symfony.lock index 8f46ac10..3b1ff310 100644 --- a/symfony.lock +++ b/symfony.lock @@ -183,6 +183,9 @@ "phpcs.xml.dist" ] }, + "symfony/ai-bundle": { + "version": "dev-main" + }, "symfony/amqp-messenger": { "version": "v6.0.1" },