Added AI embedding and phpstan analysis
This commit is contained in:
parent
65ef2ed89c
commit
a3948fe32e
@ -12,6 +12,8 @@
|
||||
"ext-pdo": "*",
|
||||
"ext-pgsql": "*",
|
||||
"doctrine/doctrine-migrations-bundle": "^3.4",
|
||||
"nyholm/psr7": "^1.8",
|
||||
"openai-php/client": "^0.13.0",
|
||||
"symfony/asset-mapper": "^7.3",
|
||||
"symfony/console": "7.2.*",
|
||||
"symfony/dotenv": "7.2.*",
|
||||
@ -24,6 +26,8 @@
|
||||
"symfony/yaml": "7.2.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpstan/phpstan-symfony": "^2.0",
|
||||
"symfony/debug-bundle": "7.2.*",
|
||||
"symfony/maker-bundle": "^1.0",
|
||||
"symfony/var-dumper": "7.2.*"
|
||||
@ -47,6 +51,7 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpstan analyse",
|
||||
"auto-scripts": {
|
||||
"cache:clear": "symfony-cmd",
|
||||
"assets:install %PUBLIC_DIR%": "symfony-cmd",
|
||||
@ -59,4 +64,4 @@
|
||||
"@auto-scripts"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
595
composer.lock
generated
595
composer.lock
generated
@ -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": "af2c42f4eb216435ec2aed6bb8b1dd11",
|
||||
"content-hash": "aff00a8d8bd55186f046d02ac6fd7c4d",
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer/semver",
|
||||
@ -792,6 +792,310 @@
|
||||
},
|
||||
"time": "2025-01-24T11:45:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nyholm/psr7",
|
||||
"version": "1.8.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Nyholm/psr7.git",
|
||||
"reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Nyholm/psr7/zipball/a71f2b11690f4b24d099d6b16690a90ae14fc6f3",
|
||||
"reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/http-message": "^1.1 || ^2.0"
|
||||
},
|
||||
"provide": {
|
||||
"php-http/message-factory-implementation": "1.0",
|
||||
"psr/http-factory-implementation": "1.0",
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"http-interop/http-factory-tests": "^0.9",
|
||||
"php-http/message-factory": "^1.0",
|
||||
"php-http/psr7-integration-tests": "^1.0",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
|
||||
"symfony/error-handler": "^4.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.8-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Nyholm\\Psr7\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Martijn van der Ven",
|
||||
"email": "martijn@vanderven.se"
|
||||
}
|
||||
],
|
||||
"description": "A fast PHP7 implementation of PSR-7",
|
||||
"homepage": "https://tnyholm.se",
|
||||
"keywords": [
|
||||
"psr-17",
|
||||
"psr-7"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Nyholm/psr7/issues",
|
||||
"source": "https://github.com/Nyholm/psr7/tree/1.8.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/Zegnat",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nyholm",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-09T07:06:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "openai-php/client",
|
||||
"version": "v0.13.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/openai-php/client.git",
|
||||
"reference": "399229860cea244843753bf1d9c28aee0e74c3a6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/openai-php/client/zipball/399229860cea244843753bf1d9c28aee0e74c3a6",
|
||||
"reference": "399229860cea244843753bf1d9c28aee0e74c3a6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.2.0",
|
||||
"php-http/discovery": "^1.20.0",
|
||||
"php-http/multipart-stream-builder": "^1.4.2",
|
||||
"psr/http-client": "^1.0.3",
|
||||
"psr/http-client-implementation": "^1.0.1",
|
||||
"psr/http-factory-implementation": "*",
|
||||
"psr/http-message": "^1.1.0|^2.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"guzzlehttp/guzzle": "^7.9.3",
|
||||
"guzzlehttp/psr7": "^2.7.1",
|
||||
"laravel/pint": "^1.22.0",
|
||||
"mockery/mockery": "^1.6.12",
|
||||
"nunomaduro/collision": "^8.8.0",
|
||||
"pestphp/pest": "^3.8.2|^4.0.0",
|
||||
"pestphp/pest-plugin-arch": "^3.1.1|^4.0.0",
|
||||
"pestphp/pest-plugin-type-coverage": "^3.5.1|^4.0.0",
|
||||
"phpstan/phpstan": "^1.12.25",
|
||||
"symfony/var-dumper": "^7.2.6"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/OpenAI.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"OpenAI\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nuno Maduro",
|
||||
"email": "enunomaduro@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Sandro Gehri"
|
||||
}
|
||||
],
|
||||
"description": "OpenAI PHP is a supercharged PHP API client that allows you to interact with the Open AI API",
|
||||
"keywords": [
|
||||
"GPT-3",
|
||||
"api",
|
||||
"client",
|
||||
"codex",
|
||||
"dall-e",
|
||||
"language",
|
||||
"natural",
|
||||
"openai",
|
||||
"php",
|
||||
"processing",
|
||||
"sdk"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/openai-php/client/issues",
|
||||
"source": "https://github.com/openai-php/client/tree/v0.13.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://www.paypal.com/paypalme/enunomaduro",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/gehrisandro",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nunomaduro",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-05-14T21:43:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-http/discovery",
|
||||
"version": "1.20.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-http/discovery.git",
|
||||
"reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-http/discovery/zipball/82fe4c73ef3363caed49ff8dd1539ba06044910d",
|
||||
"reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer-plugin-api": "^1.0|^2.0",
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"nyholm/psr7": "<1.0",
|
||||
"zendframework/zend-diactoros": "*"
|
||||
},
|
||||
"provide": {
|
||||
"php-http/async-client-implementation": "*",
|
||||
"php-http/client-implementation": "*",
|
||||
"psr/http-client-implementation": "*",
|
||||
"psr/http-factory-implementation": "*",
|
||||
"psr/http-message-implementation": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/composer": "^1.0.2|^2.0",
|
||||
"graham-campbell/phpspec-skip-example-extension": "^5.0",
|
||||
"php-http/httplug": "^1.0 || ^2.0",
|
||||
"php-http/message-factory": "^1.0",
|
||||
"phpspec/phpspec": "^5.1 || ^6.1 || ^7.3",
|
||||
"sebastian/comparator": "^3.0.5 || ^4.0.8",
|
||||
"symfony/phpunit-bridge": "^6.4.4 || ^7.0.1"
|
||||
},
|
||||
"type": "composer-plugin",
|
||||
"extra": {
|
||||
"class": "Http\\Discovery\\Composer\\Plugin",
|
||||
"plugin-optional": true
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Http\\Discovery\\": "src/"
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"src/Composer/Plugin.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations",
|
||||
"homepage": "http://php-http.org",
|
||||
"keywords": [
|
||||
"adapter",
|
||||
"client",
|
||||
"discovery",
|
||||
"factory",
|
||||
"http",
|
||||
"message",
|
||||
"psr17",
|
||||
"psr7"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-http/discovery/issues",
|
||||
"source": "https://github.com/php-http/discovery/tree/1.20.0"
|
||||
},
|
||||
"time": "2024-10-02T11:20:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-http/multipart-stream-builder",
|
||||
"version": "1.4.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-http/multipart-stream-builder.git",
|
||||
"reference": "10086e6de6f53489cca5ecc45b6f468604d3460e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-http/multipart-stream-builder/zipball/10086e6de6f53489cca5ecc45b6f468604d3460e",
|
||||
"reference": "10086e6de6f53489cca5ecc45b6f468604d3460e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0",
|
||||
"php-http/discovery": "^1.15",
|
||||
"psr/http-factory-implementation": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nyholm/psr7": "^1.0",
|
||||
"php-http/message": "^1.5",
|
||||
"php-http/message-factory": "^1.0.2",
|
||||
"phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Http\\Message\\MultipartStream\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A builder class that help you create a multipart stream",
|
||||
"homepage": "http://php-http.org",
|
||||
"keywords": [
|
||||
"factory",
|
||||
"http",
|
||||
"message",
|
||||
"multipart stream",
|
||||
"stream"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-http/multipart-stream-builder/issues",
|
||||
"source": "https://github.com/php-http/multipart-stream-builder/tree/1.4.2"
|
||||
},
|
||||
"time": "2024-09-04T13:22:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/cache",
|
||||
"version": "3.0.0",
|
||||
@ -944,6 +1248,166 @@
|
||||
},
|
||||
"time": "2019-01-08T18:20:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-client",
|
||||
"version": "1.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-client.git",
|
||||
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0 || ^8.0",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Client\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP clients",
|
||||
"homepage": "https://github.com/php-fig/http-client",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-client",
|
||||
"psr",
|
||||
"psr-18"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-client"
|
||||
},
|
||||
"time": "2023-09-23T14:17:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-factory",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-factory.git",
|
||||
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
|
||||
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
|
||||
"keywords": [
|
||||
"factory",
|
||||
"http",
|
||||
"message",
|
||||
"psr",
|
||||
"psr-17",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-factory"
|
||||
},
|
||||
"time": "2024-04-15T12:06:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
"homepage": "https://github.com/php-fig/http-message",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-message/tree/2.0"
|
||||
},
|
||||
"time": "2023-04-04T09:54:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "3.0.2",
|
||||
@ -4410,6 +4874,135 @@
|
||||
},
|
||||
"time": "2024-12-30T11:07:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.17",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "89b5ef665716fa2a52ecd2633f21007a6a349053"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/89b5ef665716fa2a52ecd2633f21007a6a349053",
|
||||
"reference": "89b5ef665716fa2a52ecd2633f21007a6a349053",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.4|^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpstan/phpstan-shim": "*"
|
||||
},
|
||||
"bin": [
|
||||
"phpstan",
|
||||
"phpstan.phar"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"keywords": [
|
||||
"dev",
|
||||
"static analysis"
|
||||
],
|
||||
"support": {
|
||||
"docs": "https://phpstan.org/user-guide/getting-started",
|
||||
"forum": "https://github.com/phpstan/phpstan/discussions",
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"security": "https://github.com/phpstan/phpstan/security/policy",
|
||||
"source": "https://github.com/phpstan/phpstan-src"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/ondrejmirtes",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/phpstan",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-05-21T20:55:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-symfony",
|
||||
"version": "2.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-symfony.git",
|
||||
"reference": "5005288e07583546ea00b52de4a9ac412eb869d7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/5005288e07583546ea00b52de4a9ac412eb869d7",
|
||||
"reference": "5005288e07583546ea00b52de4a9ac412eb869d7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-simplexml": "*",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"phpstan/phpstan": "^2.1.13"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/framework-bundle": "<3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-parallel-lint/php-parallel-lint": "^1.2",
|
||||
"phpstan/phpstan-phpunit": "^2.0",
|
||||
"phpstan/phpstan-strict-rules": "^2.0",
|
||||
"phpunit/phpunit": "^9.6",
|
||||
"psr/container": "1.1.2",
|
||||
"symfony/config": "^5.4 || ^6.1",
|
||||
"symfony/console": "^5.4 || ^6.1",
|
||||
"symfony/dependency-injection": "^5.4 || ^6.1",
|
||||
"symfony/form": "^5.4 || ^6.1",
|
||||
"symfony/framework-bundle": "^5.4 || ^6.1",
|
||||
"symfony/http-foundation": "^5.4 || ^6.1",
|
||||
"symfony/messenger": "^5.4",
|
||||
"symfony/polyfill-php80": "^1.24",
|
||||
"symfony/serializer": "^5.4",
|
||||
"symfony/service-contracts": "^2.2.0"
|
||||
},
|
||||
"type": "phpstan-extension",
|
||||
"extra": {
|
||||
"phpstan": {
|
||||
"includes": [
|
||||
"extension.neon",
|
||||
"rules.neon"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PHPStan\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Lukáš Unger",
|
||||
"email": "looky.msc@gmail.com",
|
||||
"homepage": "https://lookyman.net"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Framework extensions and rules for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-symfony/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.6"
|
||||
},
|
||||
"time": "2025-05-14T07:00:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/debug-bundle",
|
||||
"version": "v7.2.0",
|
||||
|
||||
10
config/packages/http_discovery.yaml
Normal file
10
config/packages/http_discovery.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
services:
|
||||
Psr\Http\Message\RequestFactoryInterface: '@http_discovery.psr17_factory'
|
||||
Psr\Http\Message\ResponseFactoryInterface: '@http_discovery.psr17_factory'
|
||||
Psr\Http\Message\ServerRequestFactoryInterface: '@http_discovery.psr17_factory'
|
||||
Psr\Http\Message\StreamFactoryInterface: '@http_discovery.psr17_factory'
|
||||
Psr\Http\Message\UploadedFileFactoryInterface: '@http_discovery.psr17_factory'
|
||||
Psr\Http\Message\UriFactoryInterface: '@http_discovery.psr17_factory'
|
||||
|
||||
http_discovery.psr17_factory:
|
||||
class: Http\Discovery\Psr17Factory
|
||||
3
config/secrets/dev/dev.OPENAI_API_KEY.66949e.php
Normal file
3
config/secrets/dev/dev.OPENAI_API_KEY.66949e.php
Normal file
@ -0,0 +1,3 @@
|
||||
<?php // dev.OPENAI_API_KEY.66949e on Fri, 30 May 2025 19:25:09 +0000
|
||||
|
||||
return "\x13\xABF\xCB\x3C\xE8\x5B\xBB\xD1\x5D\x09\xAEe\x26\x04o\x7B.\xEF\xCB\xCF3\x01N\xFEh\x93\x2C\xD2\xD4\x3B\x1E\xD4\xF0\xBD\x3F\x17jO5\xD9\x02F\x93Z\xB6\xDEN\x18\xECI\xDC6vh\x2C\x11\xBFS\xB1\x04\xE0\xB8q\xF1m\xD48\x1D\x7B0\x2B\xB9\x1C\xB1\x8A\x3D\xFE\x8F\xC6ux\x8D\x07\xD1\xB0\x7C\x8C\x80\xB6\xB0o\xDB\x7B\xF1O\x1C\x26\xA6\x22\xBABu\xD9\x2B\x2A\x5Dy\xFC\xB5l\xE7\x0F\x1D\xFD\xDB\x16\xFE\xED\x3D\x5D\x04\x5B\x1C\xCC\x8B\x0D\xE9OC\xE9\xE524\xD52\x28\x13\x3E\xD1b\xA8-\x97\xDFm\xAA\x92D\xE9\xFA\x3B\xD3b\xF7\xA0\xEB\x8D\xE2o\xD0\xC8\x0Cn\x8AP\x87\xF3\x93\x23\xB1\xFC\x85\xD3\xCA\xA7\xAE\x15P\xA8S\xB6\x10\xD8\xAA\x91\x2C\x94\xB6vG\x12\xDE\xE9\x2F\xFA\xAE\xB5\xD0X4\x06rq\xE3\xC4Ad\x99\xA0\xF0\x01";
|
||||
4
config/secrets/dev/dev.decrypt.private.php
Normal file
4
config/secrets/dev/dev.decrypt.private.php
Normal file
@ -0,0 +1,4 @@
|
||||
<?php // dev.decrypt.private on Fri, 30 May 2025 19:25:09 +0000
|
||||
|
||||
// SYMFONY_DECRYPTION_SECRET=MEmDRd3Ow/LTcmFwCZzQpyTZ8ZYh5zAFgpxkvzsHCzslofchkhzLbDqY85F5cAFxIokpFudvGr99tO3hHdnPDg==
|
||||
return "0I\x83E\xDD\xCE\xC3\xF2\xD3rap\x09\x9C\xD0\xA7\x24\xD9\xF1\x96\x21\xE70\x05\x82\x9Cd\xBF\x3B\x07\x0B\x3B\x25\xA1\xF7\x21\x92\x1C\xCBl\x3A\x98\xF3\x91yp\x01q\x22\x89\x29\x16\xE7o\x1A\xBF\x7D\xB4\xED\xE1\x1D\xD9\xCF\x0E";
|
||||
3
config/secrets/dev/dev.encrypt.public.php
Normal file
3
config/secrets/dev/dev.encrypt.public.php
Normal file
@ -0,0 +1,3 @@
|
||||
<?php // dev.encrypt.public on Fri, 30 May 2025 19:25:09 +0000
|
||||
|
||||
return "\x25\xA1\xF7\x21\x92\x1C\xCBl\x3A\x98\xF3\x91yp\x01q\x22\x89\x29\x16\xE7o\x1A\xBF\x7D\xB4\xED\xE1\x1D\xD9\xCF\x0E";
|
||||
5
config/secrets/dev/dev.list.php
Normal file
5
config/secrets/dev/dev.list.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'OPENAI_API_KEY' => null,
|
||||
];
|
||||
@ -10,4 +10,8 @@ services:
|
||||
exclude:
|
||||
- '../src/DependencyInjection/'
|
||||
- '../src/Entity/'
|
||||
- '../src/Kernel.php'
|
||||
- '../src/Kernel.php'
|
||||
|
||||
App\Domain\AI\AIClient:
|
||||
arguments:
|
||||
$apiKey: '%env(OPENAI_API_KEY)%'
|
||||
@ -17,7 +17,7 @@ services:
|
||||
- proxy
|
||||
|
||||
database:
|
||||
image: postgres:17-alpine
|
||||
image: pgvector/pgvector:pg17
|
||||
hostname: database.evwiki.test
|
||||
environment:
|
||||
- POSTGRES_USER=postgres
|
||||
|
||||
37
migrations/Version20250530193246.php
Normal file
37
migrations/Version20250530193246.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20250530193246 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Create embeddings table';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql(<<<SQL
|
||||
CREATE TABLE embeddings (
|
||||
phrase_hash VARCHAR(255) NOT NULL PRIMARY KEY,
|
||||
phrase VARCHAR(255) NOT NULL,
|
||||
large_embedding_vector VECTOR(3072),
|
||||
small_embedding_vector VECTOR(1536),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
SQL);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP TABLE embeddings');
|
||||
}
|
||||
}
|
||||
16
phpstan.neon
Normal file
16
phpstan.neon
Normal file
@ -0,0 +1,16 @@
|
||||
includes:
|
||||
- vendor/phpstan/phpstan-symfony/extension.neon
|
||||
|
||||
parameters:
|
||||
level: 10
|
||||
paths:
|
||||
- src
|
||||
- bin
|
||||
excludePaths:
|
||||
- src/Kernel.php (?)
|
||||
- var/*
|
||||
- vendor/*
|
||||
|
||||
# Bootstrap file for better analysis
|
||||
bootstrapFiles:
|
||||
- vendor/autoload.php
|
||||
65
src/Application/Commands/AIClientCommand.php
Normal file
65
src/Application/Commands/AIClientCommand.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Application\Commands;
|
||||
|
||||
use App\Domain\AI\AIClient;
|
||||
use App\Domain\Model\Persistence\PersistedEmbedding;
|
||||
use App\Domain\Repository\EmbeddingRepository;
|
||||
use App\Domain\Model\AI\Embedding;
|
||||
use App\Domain\Model\AI\LargeEmbeddingVector;
|
||||
use App\Domain\Model\Value\Vector;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'app:ai-client',
|
||||
description: 'AIClient command'
|
||||
)]
|
||||
class AIClientCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private readonly AIClient $aiClient,
|
||||
private readonly EmbeddingRepository $embeddingRepository,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('text', InputArgument::REQUIRED, 'The text to embed');
|
||||
$this->addOption('embed', null, InputOption::VALUE_NONE, 'Embed the text');
|
||||
$this->addOption('search', null, InputOption::VALUE_NONE, 'Search for similar texts');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$textArg = $input->getArgument('text');
|
||||
$text = is_string($textArg) ? $textArg : '';
|
||||
|
||||
if ($input->getOption('embed')) {
|
||||
$persistedEmbedding = $this->embedText($text);
|
||||
$output->writeln($persistedEmbedding->phraseHash);
|
||||
} else if ($input->getOption('search')) {
|
||||
$results = $this->embeddingRepository->searchByLargeEmbeddingVector(new LargeEmbeddingVector(new Vector($this->aiClient->embedText($text))));
|
||||
foreach ($results as $result) {
|
||||
$output->writeln($result->embedding->phrase);
|
||||
}
|
||||
} else {
|
||||
$output->writeln($this->aiClient->generateText($text));
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function embedText(string $text): PersistedEmbedding
|
||||
{
|
||||
$embedding = $this->aiClient->embedText($text);
|
||||
$vector = new Vector($embedding);
|
||||
|
||||
return $this->embeddingRepository->create(new Embedding($text, new LargeEmbeddingVector($vector)));
|
||||
}
|
||||
}
|
||||
@ -37,7 +37,9 @@ use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
)]
|
||||
class LoadFixtures extends Command
|
||||
{
|
||||
/** @var array<string, string> */
|
||||
private array $brandIds = [];
|
||||
/** @var array<string, string> */
|
||||
private array $carModelIds = [];
|
||||
|
||||
public function __construct(
|
||||
@ -73,11 +75,14 @@ class LoadFixtures extends Command
|
||||
$io->progressStart(count($carModels));
|
||||
|
||||
foreach ($carModels as $carModelData) {
|
||||
$model = $carModelData['model'];
|
||||
$brandName = $carModelData['brand'];
|
||||
|
||||
$persistedCarModel = $this->carModelRepository->create(
|
||||
$carModelData['model'],
|
||||
$this->brandIds[$carModelData['brand']]
|
||||
$model,
|
||||
$this->brandIds[$brandName]
|
||||
);
|
||||
$this->carModelIds[$carModelData['model']->name] = $persistedCarModel->id;
|
||||
$this->carModelIds[$model->name] = $persistedCarModel->id;
|
||||
$io->progressAdvance();
|
||||
}
|
||||
|
||||
@ -90,9 +95,12 @@ class LoadFixtures extends Command
|
||||
$io->progressStart(count($carRevisions));
|
||||
|
||||
foreach ($carRevisions as $carRevisionData) {
|
||||
$revision = $carRevisionData['revision'];
|
||||
$modelName = $carRevisionData['model'];
|
||||
|
||||
$this->carRevisionRepository->create(
|
||||
$carRevisionData['revision'],
|
||||
$this->carModelIds[$carRevisionData['model']]
|
||||
$revision,
|
||||
$this->carModelIds[$modelName]
|
||||
);
|
||||
$io->progressAdvance();
|
||||
}
|
||||
|
||||
41
src/Domain/AI/AIClient.php
Normal file
41
src/Domain/AI/AIClient.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\AI;
|
||||
|
||||
use OpenAI;
|
||||
|
||||
class AIClient
|
||||
{
|
||||
private readonly OpenAI\Client $client;
|
||||
|
||||
public function __construct(
|
||||
private readonly string $apiKey,
|
||||
) {
|
||||
$this->client = OpenAI::client($this->apiKey);
|
||||
}
|
||||
|
||||
public function generateText(string $prompt): string
|
||||
{
|
||||
$response = $this->client->chat()->create([
|
||||
'model' => 'gpt-4o-mini',
|
||||
'messages' => [
|
||||
['role' => 'user', 'content' => $prompt],
|
||||
],
|
||||
]);
|
||||
|
||||
return $response->choices[0]->message->content ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float[]
|
||||
*/
|
||||
public function embedText(string $text): array
|
||||
{
|
||||
$response = $this->client->embeddings()->create([
|
||||
'model' => 'text-embedding-3-large',
|
||||
'input' => $text,
|
||||
]);
|
||||
|
||||
return $response->embeddings[0]->embedding;
|
||||
}
|
||||
}
|
||||
16
src/Domain/Model/AI/Embedding.php
Normal file
16
src/Domain/Model/AI/Embedding.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Model\AI;
|
||||
|
||||
class Embedding
|
||||
{
|
||||
public function __construct(
|
||||
public readonly string $phrase,
|
||||
public readonly ?LargeEmbeddingVector $largeEmbeddingVector = null,
|
||||
public readonly ?SmallEmbeddingVector $smallEmbeddingVector = null,
|
||||
) {
|
||||
if ($largeEmbeddingVector === null && $smallEmbeddingVector === null) {
|
||||
throw new \InvalidArgumentException('At least one embedding vector must be provided');
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/Domain/Model/AI/LargeEmbeddingVector.php
Normal file
18
src/Domain/Model/AI/LargeEmbeddingVector.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Model\AI;
|
||||
|
||||
use App\Domain\Model\Value\Vector;
|
||||
|
||||
class LargeEmbeddingVector
|
||||
{
|
||||
public const DIMENSION = 3072;
|
||||
|
||||
public function __construct(
|
||||
public readonly Vector $vector,
|
||||
) {
|
||||
if ($vector->dimension() !== self::DIMENSION) {
|
||||
throw new \InvalidArgumentException('Vector must be ' . self::DIMENSION . ' dimensions');
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/Domain/Model/AI/SmallEmbeddingVector.php
Normal file
18
src/Domain/Model/AI/SmallEmbeddingVector.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Model\AI;
|
||||
|
||||
use App\Domain\Model\Value\Vector;
|
||||
|
||||
class SmallEmbeddingVector
|
||||
{
|
||||
public const DIMENSION = 1536;
|
||||
|
||||
public function __construct(
|
||||
public readonly Vector $vector,
|
||||
) {
|
||||
if ($vector->dimension() !== self::DIMENSION) {
|
||||
throw new \InvalidArgumentException('Vector must be ' . self::DIMENSION . ' dimensions');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,11 +4,17 @@ namespace App\Domain\Model;
|
||||
|
||||
class BrandCollection
|
||||
{
|
||||
/**
|
||||
* @param Brand[] $brands
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly array $brands,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Brand[]
|
||||
*/
|
||||
public function array(): array
|
||||
{
|
||||
return $this->brands;
|
||||
|
||||
@ -4,11 +4,17 @@ namespace App\Domain\Model;
|
||||
|
||||
class CarModelCollection
|
||||
{
|
||||
/**
|
||||
* @param CarModel[] $carModels
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly array $carModels,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CarModel[]
|
||||
*/
|
||||
public function array(): array
|
||||
{
|
||||
return $this->carModels;
|
||||
|
||||
@ -4,11 +4,17 @@ namespace App\Domain\Model;
|
||||
|
||||
class CarRevisionCollection
|
||||
{
|
||||
/**
|
||||
* @param CarRevision[] $carRevisions
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly array $carRevisions,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CarRevision[]
|
||||
*/
|
||||
public function array(): array
|
||||
{
|
||||
return $this->carRevisions;
|
||||
|
||||
14
src/Domain/Model/Persistence/PersistedEmbedding.php
Normal file
14
src/Domain/Model/Persistence/PersistedEmbedding.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Model\Persistence;
|
||||
|
||||
use App\Domain\Model\AI\Embedding;
|
||||
|
||||
class PersistedEmbedding
|
||||
{
|
||||
public function __construct(
|
||||
public readonly string $phraseHash,
|
||||
public readonly Embedding $embedding,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ namespace App\Domain\Model;
|
||||
|
||||
use App\Domain\Model\Range\NefzRange;
|
||||
use App\Domain\Model\Range\WltpRange;
|
||||
use App\Domain\Model\Range\RealRange;
|
||||
|
||||
final readonly class RangeProperties
|
||||
{
|
||||
|
||||
19
src/Domain/Model/Value/Vector.php
Normal file
19
src/Domain/Model/Value/Vector.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Model\Value;
|
||||
|
||||
class Vector
|
||||
{
|
||||
/**
|
||||
* @param float[] $values
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly array $values,
|
||||
) {
|
||||
}
|
||||
|
||||
public function dimension(): int
|
||||
{
|
||||
return count($this->values);
|
||||
}
|
||||
}
|
||||
30
src/Domain/Repository/EmbeddingRepository.php
Normal file
30
src/Domain/Repository/EmbeddingRepository.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Repository;
|
||||
|
||||
use App\Domain\Model\AI\Embedding;
|
||||
use App\Domain\Model\AI\LargeEmbeddingVector;
|
||||
use App\Domain\Model\AI\SmallEmbeddingVector;
|
||||
use App\Domain\Model\Persistence\PersistedEmbedding;
|
||||
use App\Domain\Model\Value\Vector;
|
||||
|
||||
interface EmbeddingRepository
|
||||
{
|
||||
public function create(Embedding $embedding): PersistedEmbedding;
|
||||
|
||||
public function delete(PersistedEmbedding $persistedEmbedding): void;
|
||||
|
||||
/**
|
||||
* @param LargeEmbeddingVector $embeddingVector
|
||||
* @param int $limit
|
||||
* @return PersistedEmbedding[]
|
||||
*/
|
||||
public function searchByLargeEmbeddingVector(LargeEmbeddingVector $embeddingVector, int $limit = 10): array;
|
||||
|
||||
/**
|
||||
* @param SmallEmbeddingVector $vector
|
||||
* @param int $limit
|
||||
* @return PersistedEmbedding[]
|
||||
*/
|
||||
public function searchBySmallEmbeddingVector(SmallEmbeddingVector $vector, int $limit = 10): array;
|
||||
}
|
||||
@ -134,36 +134,36 @@ class Engine
|
||||
|
||||
return new TileCollection([
|
||||
new SectionTile('Skoda Enyaq iV 85', [
|
||||
new CarTile($skodaElroq85->image, [
|
||||
new CarTile($skodaElroq85->image ?? new Image(), array_filter([
|
||||
new BrandTile('Skoda'),
|
||||
new PriceTile($skodaElroq85->catalogPrice),
|
||||
$skodaElroq85->catalogPrice ? new PriceTile($skodaElroq85->catalogPrice) : null,
|
||||
new AvailabilityTile('Verfügbar', new Date(1, 1, 2020)),
|
||||
new RangeTile($wltpRange->range),
|
||||
new ConsumptionTile($drivingCharacteristics->consumption),
|
||||
new AccelerationTile($drivingCharacteristics->acceleration),
|
||||
]),
|
||||
$drivingCharacteristics->consumption ? new ConsumptionTile($drivingCharacteristics->consumption) : null,
|
||||
$drivingCharacteristics->acceleration ? new AccelerationTile($drivingCharacteristics->acceleration) : null,
|
||||
])),
|
||||
|
||||
new SubSectionTile('Performance', [
|
||||
new PowerTile($drivingCharacteristics->power),
|
||||
new TopSpeedTile($drivingCharacteristics->topSpeed),
|
||||
new SubSectionTile('Performance', array_filter([
|
||||
$drivingCharacteristics->power ? new PowerTile($drivingCharacteristics->power) : null,
|
||||
$drivingCharacteristics->topSpeed ? new TopSpeedTile($drivingCharacteristics->topSpeed) : null,
|
||||
new DrivetrainTile(new Drivetrain('rear')),
|
||||
], 'Individual performance metrics'),
|
||||
])),
|
||||
|
||||
new SubSectionTile('Reichweite', [
|
||||
new RangeTile($wltpRange->range),
|
||||
new RealRangeTile($realRangeTests),
|
||||
], 'Range data from different sources'),
|
||||
]),
|
||||
|
||||
new SubSectionTile('Batterie', [
|
||||
new BatteryTile($skodaElroq85->battery),
|
||||
new BatteryDetailsTile($skodaElroq85->battery),
|
||||
], 'Battery capacity and technology'),
|
||||
new SubSectionTile('Batterie', array_filter([
|
||||
$skodaElroq85->battery ? new BatteryTile($skodaElroq85->battery) : null,
|
||||
$skodaElroq85->battery ? new BatteryDetailsTile($skodaElroq85->battery) : null,
|
||||
])),
|
||||
|
||||
new SubSectionTile('Laden', [
|
||||
new SubSectionTile('Laden', array_filter([
|
||||
new ChargingTile($chargingSpeed),
|
||||
new ChargeTimeTile($chargingProperties->chargeTimeProperties),
|
||||
new ChargingConnectivityTile($chargingProperties->chargingConnectivity),
|
||||
], 'Charging capabilities and compatibility'),
|
||||
$chargingProperties->chargeTimeProperties ? new ChargeTimeTile($chargingProperties->chargeTimeProperties) : null,
|
||||
$chargingProperties->chargingConnectivity ? new ChargingConnectivityTile($chargingProperties->chargingConnectivity) : null,
|
||||
])),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -6,10 +6,16 @@ use App\Domain\Search\Tiles\SectionTile;
|
||||
|
||||
class TileCollection
|
||||
{
|
||||
/**
|
||||
* @param SectionTile[] $tiles
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly array $tiles,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return SectionTile[]
|
||||
*/
|
||||
public function array(): array
|
||||
{
|
||||
return $this->tiles;
|
||||
|
||||
@ -6,6 +6,9 @@ use App\Domain\Model\Image;
|
||||
|
||||
final readonly class CarTile
|
||||
{
|
||||
/**
|
||||
* @param object[] $tiles
|
||||
*/
|
||||
public function __construct(
|
||||
public Image $image,
|
||||
public array $tiles
|
||||
|
||||
@ -4,6 +4,9 @@ namespace App\Domain\Search\Tiles;
|
||||
|
||||
class SectionTile
|
||||
{
|
||||
/**
|
||||
* @param object[] $tiles
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly string $title,
|
||||
public readonly array $tiles,
|
||||
|
||||
@ -4,6 +4,9 @@ namespace App\Domain\Search\Tiles;
|
||||
|
||||
class SubSectionTile
|
||||
{
|
||||
/**
|
||||
* @param object[] $tiles
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly string $title,
|
||||
public readonly array $tiles,
|
||||
|
||||
@ -6,17 +6,18 @@ use App\Domain\Model\Brand;
|
||||
|
||||
class ModelMapper
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
public function map(array $data): Brand
|
||||
{
|
||||
return new Brand(
|
||||
id: (string) ($data['id'] ?? null),
|
||||
name: $data['name'] ?? '',
|
||||
logo: $data['logo'] ?? '',
|
||||
description: $data['description'] ?? '',
|
||||
foundedYear: (int) ($data['founded_year'] ?? 0),
|
||||
headquarters: $data['headquarters'] ?? '',
|
||||
website: $data['website'] ?? '',
|
||||
carModels: json_decode($data['car_models'] ?? '[]', true),
|
||||
name: is_string($data['name'] ?? null) ? $data['name'] : '',
|
||||
logo: isset($data['logo']) && is_string($data['logo']) ? $data['logo'] : null,
|
||||
description: isset($data['description']) && is_string($data['description']) ? $data['description'] : null,
|
||||
foundedYear: isset($data['founded_year']) && is_numeric($data['founded_year']) ? (int) $data['founded_year'] : null,
|
||||
headquarters: isset($data['headquarters']) && is_string($data['headquarters']) ? $data['headquarters'] : null,
|
||||
website: isset($data['website']) && is_string($data['website']) ? $data['website'] : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -23,7 +23,7 @@ final class PostgreSQLBrandRepository implements BrandRepository
|
||||
$brands = [];
|
||||
$mapper = new ModelMapper();
|
||||
|
||||
foreach ($result as $brand) {
|
||||
foreach ($result->fetchAllAssociative() as $brand) {
|
||||
$brands[] = $mapper->map($brand);
|
||||
}
|
||||
|
||||
|
||||
@ -7,17 +7,21 @@ use App\Domain\Model\Brand;
|
||||
|
||||
class ModelMapper
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
public function map(array $data): CarModel
|
||||
{
|
||||
$content = json_decode($data['content'] ?? '{}', true);
|
||||
$contentString = is_string($data['content'] ?? null) ? $data['content'] : '{}';
|
||||
$content = json_decode($contentString, true);
|
||||
|
||||
$brand = null;
|
||||
if (!empty($content['brand'])) {
|
||||
$brand = new Brand($content['brand']);
|
||||
if (is_array($content) && !empty($content['brand']) && is_string($content['brand'])) {
|
||||
$brand = new Brand(name: $content['brand']);
|
||||
}
|
||||
|
||||
return new CarModel(
|
||||
name: $data['name'] ?? '',
|
||||
name: is_string($data['name'] ?? null) ? $data['name'] : '',
|
||||
brand: $brand,
|
||||
);
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ final class PostgreSQLCarModelRepository implements CarModelRepository
|
||||
$carModels = [];
|
||||
$mapper = new ModelMapper();
|
||||
|
||||
foreach ($result as $carModel) {
|
||||
foreach ($result->fetchAllAssociative() as $carModel) {
|
||||
$carModels[] = $mapper->map($carModel);
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ final class PostgreSQLCarModelRepository implements CarModelRepository
|
||||
$mapper = new ModelMapper();
|
||||
$carModel = $mapper->map($data);
|
||||
|
||||
return new PersistedCarModel($data['id'], $carModel);
|
||||
return new PersistedCarModel(is_string($data['id'] ?? null) ? $data['id'] : '', $carModel);
|
||||
}
|
||||
|
||||
public function findByBrandId(string $brandId): CarModelCollection
|
||||
@ -54,7 +54,7 @@ final class PostgreSQLCarModelRepository implements CarModelRepository
|
||||
$carModels = [];
|
||||
$mapper = new ModelMapper();
|
||||
|
||||
foreach ($result as $carModel) {
|
||||
foreach ($result->fetchAllAssociative() as $carModel) {
|
||||
$carModels[] = $mapper->map($carModel);
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ final class PostgreSQLCarModelRepository implements CarModelRepository
|
||||
SQL;
|
||||
|
||||
$content = json_encode([
|
||||
'brand' => $carModel->brand?->name ?? null,
|
||||
'brand' => $carModel->brand->name ?? null,
|
||||
]);
|
||||
|
||||
$this->connection->executeStatement($sql, [
|
||||
@ -101,7 +101,7 @@ final class PostgreSQLCarModelRepository implements CarModelRepository
|
||||
SQL;
|
||||
|
||||
$content = json_encode([
|
||||
'brand' => $carModel->brand?->name ?? null,
|
||||
'brand' => $carModel->brand->name ?? null,
|
||||
]);
|
||||
|
||||
$this->connection->executeStatement($sql, [
|
||||
|
||||
@ -23,22 +23,31 @@ use App\Domain\Model\Value\Range;
|
||||
|
||||
class ModelMapper
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
public function map(array $data): CarRevision
|
||||
{
|
||||
$content = json_decode($data['content'] ?? '{}', true);
|
||||
$contentString = is_string($data['content'] ?? null) ? $data['content'] : '{}';
|
||||
$content = json_decode($contentString, true);
|
||||
|
||||
if (!is_array($content)) {
|
||||
$content = [];
|
||||
}
|
||||
|
||||
$productionBegin = null;
|
||||
if (!empty($content['production_begin'])) {
|
||||
$productionBegin = new Date(1, 1, $content['production_begin']);
|
||||
if (isset($content['production_begin']) && is_numeric($content['production_begin'])) {
|
||||
$productionBegin = new Date(1, 1, (int) $content['production_begin']);
|
||||
}
|
||||
|
||||
$productionEnd = null;
|
||||
if (!empty($content['production_end'])) {
|
||||
$productionEnd = new Date(1, 1, $content['production_end']);
|
||||
if (isset($content['production_end']) && is_numeric($content['production_end'])) {
|
||||
$productionEnd = new Date(1, 1, (int) $content['production_end']);
|
||||
}
|
||||
|
||||
$catalogPrice = null;
|
||||
if (!empty($content['catalog_price']) && !empty($content['catalog_price_currency'])) {
|
||||
if (isset($content['catalog_price']) && isset($content['catalog_price_currency']) &&
|
||||
is_numeric($content['catalog_price']) && is_string($content['catalog_price_currency'])) {
|
||||
$currency = match($content['catalog_price_currency']) {
|
||||
'EUR' => Currency::euro(),
|
||||
'USD' => Currency::usd(),
|
||||
@ -46,53 +55,56 @@ class ModelMapper
|
||||
};
|
||||
|
||||
$catalogPrice = new Price(
|
||||
$content['catalog_price'],
|
||||
(int) $content['catalog_price'],
|
||||
$currency
|
||||
);
|
||||
}
|
||||
|
||||
$image = null;
|
||||
if (!empty($content['image_url'])) {
|
||||
if (isset($content['image_url']) && is_string($content['image_url'])) {
|
||||
$image = new Image($content['image_url']);
|
||||
}
|
||||
|
||||
$drivingCharacteristics = null;
|
||||
if (!empty($content['driving_characteristics'])) {
|
||||
if (isset($content['driving_characteristics']) && is_array($content['driving_characteristics'])) {
|
||||
$dc = $content['driving_characteristics'];
|
||||
$drivingCharacteristics = new DrivingCharacteristics(
|
||||
power: !empty($dc['power_kw']) ? new Power($dc['power_kw']) : null,
|
||||
acceleration: !empty($dc['acceleration_0_100']) ? new Acceleration($dc['acceleration_0_100']) : null,
|
||||
topSpeed: !empty($dc['top_speed_kmh']) ? new Speed($dc['top_speed_kmh']) : null,
|
||||
consumption: !empty($dc['consumption_kwh_100km']) ? new Consumption(new Energy($dc['consumption_kwh_100km'])) : null,
|
||||
power: (isset($dc['power_kw']) && is_numeric($dc['power_kw'])) ? new Power((float) $dc['power_kw']) : null,
|
||||
acceleration: (isset($dc['acceleration_0_100']) && is_numeric($dc['acceleration_0_100'])) ? new Acceleration((float) $dc['acceleration_0_100']) : null,
|
||||
topSpeed: (isset($dc['top_speed_kmh']) && is_numeric($dc['top_speed_kmh'])) ? new Speed((int) $dc['top_speed_kmh']) : null,
|
||||
consumption: (isset($dc['consumption_kwh_100km']) && is_numeric($dc['consumption_kwh_100km'])) ? new Consumption(new Energy((float) $dc['consumption_kwh_100km'])) : null,
|
||||
);
|
||||
}
|
||||
|
||||
$battery = null;
|
||||
if (!empty($content['battery'])) {
|
||||
if (isset($content['battery']) && is_array($content['battery'])) {
|
||||
$b = $content['battery'];
|
||||
if (!empty($b['usable_capacity_kwh']) && !empty($b['total_capacity_kwh'])) {
|
||||
if (isset($b['usable_capacity_kwh']) && isset($b['total_capacity_kwh']) &&
|
||||
is_numeric($b['usable_capacity_kwh']) && is_numeric($b['total_capacity_kwh'])) {
|
||||
$battery = new BatteryProperties(
|
||||
usableCapacity: new Energy($b['usable_capacity_kwh']),
|
||||
totalCapacity: new Energy($b['total_capacity_kwh']),
|
||||
cellChemistry: !empty($b['cell_chemistry']) ? CellChemistry::from($b['cell_chemistry']) : CellChemistry::LithiumIronPhosphate,
|
||||
model: $b['model'] ?? '',
|
||||
manufacturer: $b['manufacturer'] ?? '',
|
||||
usableCapacity: new Energy((float) $b['usable_capacity_kwh']),
|
||||
totalCapacity: new Energy((float) $b['total_capacity_kwh']),
|
||||
cellChemistry: (isset($b['cell_chemistry']) && (is_string($b['cell_chemistry']) || is_int($b['cell_chemistry']))) ? CellChemistry::from($b['cell_chemistry']) : CellChemistry::LithiumIronPhosphate,
|
||||
model: is_string($b['model'] ?? null) ? $b['model'] : '',
|
||||
manufacturer: is_string($b['manufacturer'] ?? null) ? $b['manufacturer'] : '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$chargingProperties = null;
|
||||
if (!empty($content['charging']['top_charging_speed_kw'])) {
|
||||
if (isset($content['charging']) && is_array($content['charging']) &&
|
||||
isset($content['charging']['top_charging_speed_kw']) &&
|
||||
is_numeric($content['charging']['top_charging_speed_kw'])) {
|
||||
$chargingProperties = new ChargingProperties(
|
||||
topChargingSpeed: new Power($content['charging']['top_charging_speed_kw'])
|
||||
topChargingSpeed: new Power((float) $content['charging']['top_charging_speed_kw'])
|
||||
);
|
||||
}
|
||||
|
||||
$rangeProperties = null;
|
||||
if (!empty($content['range'])) {
|
||||
if (isset($content['range']) && is_array($content['range'])) {
|
||||
$r = $content['range'];
|
||||
$wltp = !empty($r['wltp_km']) ? new WltpRange(new Range($r['wltp_km'])) : null;
|
||||
$nefz = !empty($r['nefz_km']) ? new NefzRange(new Range($r['nefz_km'])) : null;
|
||||
$wltp = (isset($r['wltp_km']) && is_numeric($r['wltp_km'])) ? new WltpRange(new Range((int) $r['wltp_km'])) : null;
|
||||
$nefz = (isset($r['nefz_km']) && is_numeric($r['nefz_km'])) ? new NefzRange(new Range((int) $r['nefz_km'])) : null;
|
||||
|
||||
if ($wltp || $nefz) {
|
||||
$rangeProperties = new RangeProperties(
|
||||
@ -103,7 +115,7 @@ class ModelMapper
|
||||
}
|
||||
|
||||
return new CarRevision(
|
||||
name: $data['name'] ?? '',
|
||||
name: is_string($data['name'] ?? null) ? $data['name'] : '',
|
||||
productionBegin: $productionBegin,
|
||||
productionEnd: $productionEnd,
|
||||
drivingCharacteristics: $drivingCharacteristics,
|
||||
|
||||
@ -23,7 +23,7 @@ final class PostgreSQLCarRevisionRepository implements CarRevisionRepository
|
||||
$carRevisions = [];
|
||||
$mapper = new ModelMapper();
|
||||
|
||||
foreach ($result as $carRevision) {
|
||||
foreach ($result->fetchAllAssociative() as $carRevision) {
|
||||
$carRevisions[] = $mapper->map($carRevision);
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ final class PostgreSQLCarRevisionRepository implements CarRevisionRepository
|
||||
$mapper = new ModelMapper();
|
||||
$carRevision = $mapper->map($data);
|
||||
|
||||
return new PersistedCarRevision($data['id'], $carRevision);
|
||||
return new PersistedCarRevision(is_string($data['id'] ?? null) ? $data['id'] : '', $carRevision);
|
||||
}
|
||||
|
||||
public function findByCarModelId(string $carModelId): CarRevisionCollection
|
||||
@ -54,7 +54,7 @@ final class PostgreSQLCarRevisionRepository implements CarRevisionRepository
|
||||
$carRevisions = [];
|
||||
$mapper = new ModelMapper();
|
||||
|
||||
foreach ($result as $carRevision) {
|
||||
foreach ($result->fetchAllAssociative() as $carRevision) {
|
||||
$carRevisions[] = $mapper->map($carRevision);
|
||||
}
|
||||
|
||||
@ -76,26 +76,26 @@ final class PostgreSQLCarRevisionRepository implements CarRevisionRepository
|
||||
SQL;
|
||||
|
||||
$content = json_encode([
|
||||
'production_begin' => $carRevision->productionBegin?->year ?? null,
|
||||
'production_end' => $carRevision->productionEnd?->year ?? null,
|
||||
'catalog_price' => $carRevision->catalogPrice?->price ?? null,
|
||||
'production_begin' => $carRevision->productionBegin->year ?? null,
|
||||
'production_end' => $carRevision->productionEnd->year ?? null,
|
||||
'catalog_price' => $carRevision->catalogPrice->price ?? null,
|
||||
'catalog_price_currency' => $carRevision->catalogPrice?->currency->currency ?? null,
|
||||
'image_url' => $carRevision->image?->url ?? null,
|
||||
'image_url' => $carRevision->image->externalPublicUrl ?? null,
|
||||
'driving_characteristics' => [
|
||||
'power_kw' => $carRevision->drivingCharacteristics?->power?->kilowatts ?? null,
|
||||
'acceleration_0_100' => $carRevision->drivingCharacteristics?->acceleration?->secondsFrom0To100 ?? null,
|
||||
'top_speed_kmh' => $carRevision->drivingCharacteristics?->topSpeed?->kmh ?? null,
|
||||
'power_kw' => $carRevision->drivingCharacteristics->power->kilowatts ?? null,
|
||||
'acceleration_0_100' => $carRevision->drivingCharacteristics->acceleration->secondsFrom0To100 ?? null,
|
||||
'top_speed_kmh' => $carRevision->drivingCharacteristics->topSpeed->kmh ?? null,
|
||||
'consumption_kwh_100km' => $carRevision->drivingCharacteristics?->consumption?->energyPer100Km->kwh() ?? null,
|
||||
],
|
||||
'battery' => [
|
||||
'usable_capacity_kwh' => $carRevision->battery?->usableCapacity->kwh() ?? null,
|
||||
'total_capacity_kwh' => $carRevision->battery?->totalCapacity->kwh() ?? null,
|
||||
'cell_chemistry' => $carRevision->battery?->cellChemistry->value ?? null,
|
||||
'model' => $carRevision->battery?->model ?? null,
|
||||
'manufacturer' => $carRevision->battery?->manufacturer ?? null,
|
||||
'model' => $carRevision->battery->model ?? null,
|
||||
'manufacturer' => $carRevision->battery->manufacturer ?? null,
|
||||
],
|
||||
'charging' => [
|
||||
'top_charging_speed_kw' => $carRevision->chargingProperties?->topChargingSpeed?->kilowatts ?? null,
|
||||
'top_charging_speed_kw' => $carRevision->chargingProperties->topChargingSpeed->kilowatts ?? null,
|
||||
],
|
||||
'range' => [
|
||||
'wltp_km' => $carRevision->rangeProperties?->wltp?->range->kilometers ?? null,
|
||||
@ -125,26 +125,26 @@ final class PostgreSQLCarRevisionRepository implements CarRevisionRepository
|
||||
SQL;
|
||||
|
||||
$content = json_encode([
|
||||
'production_begin' => $carRevision->productionBegin?->year ?? null,
|
||||
'production_end' => $carRevision->productionEnd?->year ?? null,
|
||||
'catalog_price' => $carRevision->catalogPrice?->price ?? null,
|
||||
'production_begin' => $carRevision->productionBegin->year ?? null,
|
||||
'production_end' => $carRevision->productionEnd->year ?? null,
|
||||
'catalog_price' => $carRevision->catalogPrice->price ?? null,
|
||||
'catalog_price_currency' => $carRevision->catalogPrice?->currency->currency ?? null,
|
||||
'image_url' => $carRevision->image?->url ?? null,
|
||||
'image_url' => $carRevision->image->externalPublicUrl ?? null,
|
||||
'driving_characteristics' => [
|
||||
'power_kw' => $carRevision->drivingCharacteristics?->power?->kilowatts ?? null,
|
||||
'acceleration_0_100' => $carRevision->drivingCharacteristics?->acceleration?->secondsFrom0To100 ?? null,
|
||||
'top_speed_kmh' => $carRevision->drivingCharacteristics?->topSpeed?->kmh ?? null,
|
||||
'power_kw' => $carRevision->drivingCharacteristics->power->kilowatts ?? null,
|
||||
'acceleration_0_100' => $carRevision->drivingCharacteristics->acceleration->secondsFrom0To100 ?? null,
|
||||
'top_speed_kmh' => $carRevision->drivingCharacteristics->topSpeed->kmh ?? null,
|
||||
'consumption_kwh_100km' => $carRevision->drivingCharacteristics?->consumption?->energyPer100Km->kwh() ?? null,
|
||||
],
|
||||
'battery' => [
|
||||
'usable_capacity_kwh' => $carRevision->battery?->usableCapacity->kwh() ?? null,
|
||||
'total_capacity_kwh' => $carRevision->battery?->totalCapacity->kwh() ?? null,
|
||||
'cell_chemistry' => $carRevision->battery?->cellChemistry->value ?? null,
|
||||
'model' => $carRevision->battery?->model ?? null,
|
||||
'manufacturer' => $carRevision->battery?->manufacturer ?? null,
|
||||
'model' => $carRevision->battery->model ?? null,
|
||||
'manufacturer' => $carRevision->battery->manufacturer ?? null,
|
||||
],
|
||||
'charging' => [
|
||||
'top_charging_speed_kw' => $carRevision->chargingProperties?->topChargingSpeed?->kilowatts ?? null,
|
||||
'top_charging_speed_kw' => $carRevision->chargingProperties->topChargingSpeed->kilowatts ?? null,
|
||||
],
|
||||
'range' => [
|
||||
'wltp_km' => $carRevision->rangeProperties?->wltp?->range->kilometers ?? null,
|
||||
|
||||
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace App\Infrastructure\PostgreSQL\Repository\EmbeddingRepository;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use App\Domain\Repository\EmbeddingRepository;
|
||||
use App\Domain\Model\AI\Embedding;
|
||||
use App\Domain\Model\AI\LargeEmbeddingVector;
|
||||
use App\Domain\Model\AI\SmallEmbeddingVector;
|
||||
use App\Domain\Model\Persistence\PersistedEmbedding;
|
||||
|
||||
class PostgreSQLEmbeddingRepository implements EmbeddingRepository
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Connection $connection,
|
||||
) {
|
||||
}
|
||||
|
||||
public function create(Embedding $embedding): PersistedEmbedding
|
||||
{
|
||||
$hash = md5($embedding->phrase);
|
||||
$this->connection->executeStatement(<<<SQL
|
||||
INSERT INTO embeddings (phrase_hash, phrase, large_embedding_vector, small_embedding_vector)
|
||||
VALUES (:phrase_hash, :phrase, :large_embedding_vector, :small_embedding_vector)
|
||||
SQL, [
|
||||
'phrase_hash' => $hash,
|
||||
'phrase' => $embedding->phrase,
|
||||
'large_embedding_vector' => $embedding->largeEmbeddingVector !== null ? '[' . implode(',', $embedding->largeEmbeddingVector->vector->values) . ']' : null,
|
||||
'small_embedding_vector' => $embedding->smallEmbeddingVector !== null ? '[' . implode(',', $embedding->smallEmbeddingVector->vector->values) . ']' : null,
|
||||
]);
|
||||
|
||||
return new PersistedEmbedding(
|
||||
$hash,
|
||||
$embedding,
|
||||
);
|
||||
}
|
||||
|
||||
public function delete(PersistedEmbedding $persistedEmbedding): void
|
||||
{
|
||||
$this->connection->delete('embeddings', ['phrase_hash' => $persistedEmbedding->phraseHash]);
|
||||
}
|
||||
|
||||
public function searchByLargeEmbeddingVector(LargeEmbeddingVector $embeddingVector, int $limit = 10): array
|
||||
{
|
||||
$result = $this->connection->executeQuery(<<<SQL
|
||||
SELECT phrase_hash, phrase, large_embedding_vector, small_embedding_vector, created_at
|
||||
FROM embeddings
|
||||
WHERE large_embedding_vector IS NOT NULL
|
||||
ORDER BY large_embedding_vector <=> :vector
|
||||
LIMIT :limit
|
||||
SQL, [
|
||||
'vector' => '[' . implode(',', $embeddingVector->vector->values) . ']',
|
||||
'limit' => $limit,
|
||||
]);
|
||||
|
||||
$embeddings = [];
|
||||
foreach ($result->fetchAllAssociative() as $row) {
|
||||
$embeddings[] = new PersistedEmbedding(
|
||||
is_string($row['phrase_hash'] ?? null) ? $row['phrase_hash'] : '',
|
||||
new Embedding(is_string($row['phrase'] ?? null) ? $row['phrase'] : '', $embeddingVector)
|
||||
);
|
||||
}
|
||||
|
||||
return $embeddings;
|
||||
}
|
||||
|
||||
public function searchBySmallEmbeddingVector(SmallEmbeddingVector $smallEmbeddingVector, int $limit = 10): array
|
||||
{
|
||||
$result = $this->connection->executeQuery(<<<SQL
|
||||
SELECT phrase_hash, phrase, large_embedding_vector, small_embedding_vector, created_at
|
||||
FROM embeddings
|
||||
WHERE small_embedding_vector IS NOT NULL
|
||||
ORDER BY small_embedding_vector <=> :vector
|
||||
LIMIT :limit
|
||||
SQL, [
|
||||
'vector' => '[' . implode(',', $smallEmbeddingVector->vector->values) . ']',
|
||||
'limit' => $limit,
|
||||
]);
|
||||
|
||||
$embeddings = [];
|
||||
foreach ($result->fetchAllAssociative() as $row) {
|
||||
$embeddings[] = new PersistedEmbedding(
|
||||
is_string($row['phrase_hash'] ?? null) ? $row['phrase_hash'] : '',
|
||||
new Embedding(is_string($row['phrase'] ?? null) ? $row['phrase'] : '', null, $smallEmbeddingVector)
|
||||
);
|
||||
}
|
||||
|
||||
return $embeddings;
|
||||
}
|
||||
}
|
||||
21
symfony.lock
21
symfony.lock
@ -35,6 +35,27 @@
|
||||
"migrations/.gitignore"
|
||||
]
|
||||
},
|
||||
"php-http/discovery": {
|
||||
"version": "1.20",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "1.18",
|
||||
"ref": "f45b5dd173a27873ab19f5e3180b2f661c21de02"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/http_discovery.yaml"
|
||||
]
|
||||
},
|
||||
"phpstan/phpstan": {
|
||||
"version": "2.1",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes-contrib",
|
||||
"branch": "main",
|
||||
"version": "1.0",
|
||||
"ref": "5e490cc197fb6bb1ae22e5abbc531ddc633b6767"
|
||||
}
|
||||
},
|
||||
"symfony/asset-mapper": {
|
||||
"version": "7.3",
|
||||
"recipe": {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user