From 0df6f49bbef3a3d15bfaeff3f40a48ed56f88355 Mon Sep 17 00:00:00 2001 From: Mateusz Charytoniuk <mateusz.charytoniuk@protonmail.com> Date: Tue, 16 Jan 2024 04:05:04 +0100 Subject: [PATCH] feat: basic Ollama client --- .github/workflows/unit_tests.yaml | 2 +- composer.json | 3 +- composer.lock | 1480 +++++++++++++++-- config.ini.example | 4 + src/Command/OllamaCompletion.php | 64 + src/Command/OllamaEmbedding.php | 73 + src/CurlException.php | 20 + src/EsbuildMetaBuilder.php | 10 +- src/JsonSerializer.php | 8 + src/OllamaClient.php | 123 ++ src/OllamaCompletionRequest.php | 32 + src/OllamaConfiguration.php | 14 + src/OllamaEmbeddingRequest.php | 27 + src/OllamaEmbeddingResponse.php | 20 + src/OllamaLinkBuilder.php | 26 + src/OllamaRequestOptions.php | 28 + src/OllamaRequestStopDelimiter.php | 23 + .../OllamaConfigurationProvider.php | 59 + src/SwooleChannelIterator.php | 45 + .../RPCProtocolController.php | 7 +- 20 files changed, 1962 insertions(+), 106 deletions(-) create mode 100644 src/Command/OllamaCompletion.php create mode 100644 src/Command/OllamaEmbedding.php create mode 100644 src/CurlException.php create mode 100644 src/OllamaClient.php create mode 100644 src/OllamaCompletionRequest.php create mode 100644 src/OllamaConfiguration.php create mode 100644 src/OllamaEmbeddingRequest.php create mode 100644 src/OllamaEmbeddingResponse.php create mode 100644 src/OllamaLinkBuilder.php create mode 100644 src/OllamaRequestOptions.php create mode 100644 src/OllamaRequestStopDelimiter.php create mode 100644 src/SingletonProvider/ConfigurationProvider/OllamaConfigurationProvider.php create mode 100644 src/SwooleChannelIterator.php diff --git a/.github/workflows/unit_tests.yaml b/.github/workflows/unit_tests.yaml index f3b2fd99..b5b4c88c 100644 --- a/.github/workflows/unit_tests.yaml +++ b/.github/workflows/unit_tests.yaml @@ -44,7 +44,7 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: '8.2' - extensions: ds, inotify, uuid, swoole + extensions: curl, ds, inotify, uuid, swoole - name: checkout uses: actions/checkout@v4 diff --git a/composer.json b/composer.json index a6ce2584..fdfb0f62 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,8 @@ "webonyx/graphql-php": "^15.6", "dragonmantank/cron-expression": "^3.3", "league/oauth2-client": "^2.7", - "opis/json-schema": "^2.3" + "opis/json-schema": "^2.3", + "rubix/ml": "^2.4" }, "require-dev": { "mockery/mockery": "^1.6", diff --git a/composer.lock b/composer.lock index 44312f18..50c356a4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,564 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "54c7a151e1eb3558a606574811103c43", + "content-hash": "e70cfc3cd3185200bf00c96fe64e1db1", "packages": [ + { + "name": "amphp/amp", + "version": "v2.6.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^7 | ^8 | ^9", + "psalm/phar": "^3.11@dev", + "react/promise": "^2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php", + "lib/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.6.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-02-20T17:52:18+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v1.8.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.4", + "friendsofphp/php-cs-fixer": "^2.3", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6 || ^7 || ^8", + "psalm/phar": "^3.11.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "http://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-03-30T17:13:30+00:00" + }, + { + "name": "amphp/parallel", + "version": "v1.4.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/parallel.git", + "reference": "3aac213ba7858566fd83d38ccb85b91b2d652cb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parallel/zipball/3aac213ba7858566fd83d38ccb85b91b2d652cb0", + "reference": "3aac213ba7858566fd83d38ccb85b91b2d652cb0", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "amphp/byte-stream": "^1.6.1", + "amphp/parser": "^1", + "amphp/process": "^1", + "amphp/serialization": "^1", + "amphp/sync": "^1.0.1", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.1", + "phpunit/phpunit": "^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "lib/Context/functions.php", + "lib/Sync/functions.php", + "lib/Worker/functions.php" + ], + "psr-4": { + "Amp\\Parallel\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Parallel processing component for Amp.", + "homepage": "https://github.com/amphp/parallel", + "keywords": [ + "async", + "asynchronous", + "concurrent", + "multi-processing", + "multi-threading" + ], + "support": { + "issues": "https://github.com/amphp/parallel/issues", + "source": "https://github.com/amphp/parallel/tree/v1.4.3" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2023-03-23T08:04:23+00:00" + }, + { + "name": "amphp/parser", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/parser.git", + "reference": "ff1de4144726c5dad5fab97f66692ebe8de3e151" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parser/zipball/ff1de4144726c5dad5fab97f66692ebe8de3e151", + "reference": "ff1de4144726c5dad5fab97f66692ebe8de3e151", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Parser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", + "keywords": [ + "async", + "non-blocking", + "parser", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/parser/issues", + "source": "https://github.com/amphp/parser/tree/v1.1.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-12-30T18:08:47+00:00" + }, + { + "name": "amphp/process", + "version": "v1.1.4", + "source": { + "type": "git", + "url": "https://github.com/amphp/process.git", + "reference": "76e9495fd6818b43a20167cb11d8a67f7744ee0f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/process/zipball/76e9495fd6818b43a20167cb11d8a67f7744ee0f", + "reference": "76e9495fd6818b43a20167cb11d8a67f7744ee0f", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "amphp/byte-stream": "^1.4", + "php": ">=7" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "phpunit/phpunit": "^6" + }, + "type": "library", + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Amp\\Process\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Asynchronous process manager.", + "homepage": "https://github.com/amphp/process", + "support": { + "issues": "https://github.com/amphp/process/issues", + "source": "https://github.com/amphp/process/tree/v1.1.4" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-07-06T23:50:12+00:00" + }, + { + "name": "amphp/serialization", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/serialization.git", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "phpunit/phpunit": "^9 || ^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Serialization\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Serialization tools for IPC and data storage in PHP.", + "homepage": "https://github.com/amphp/serialization", + "keywords": [ + "async", + "asynchronous", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/amphp/serialization/issues", + "source": "https://github.com/amphp/serialization/tree/master" + }, + "time": "2020-03-25T21:39:07+00:00" + }, + { + "name": "amphp/sync", + "version": "v1.4.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/sync.git", + "reference": "85ab06764f4f36d63b1356b466df6111cf4b89cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/sync/zipball/85ab06764f4f36d63b1356b466df6111cf4b89cf", + "reference": "85ab06764f4f36d63b1356b466df6111cf4b89cf", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.1", + "phpunit/phpunit": "^9 || ^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/ConcurrentIterator/functions.php" + ], + "psr-4": { + "Amp\\Sync\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Mutex, Semaphore, and other synchronization tools for Amp.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], + "support": { + "issues": "https://github.com/amphp/sync/issues", + "source": "https://github.com/amphp/sync/tree/v1.4.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-10-25T18:29:10+00:00" + }, + { + "name": "andrewdalpino/okbloomer", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/andrewdalpino/OkBloomer.git", + "reference": "39321cb515c1e99128d28489b0187120ba7ce84c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/andrewdalpino/OkBloomer/zipball/39321cb515c1e99128d28489b0187120ba7ce84c", + "reference": "39321cb515c1e99128d28489b0187120ba7ce84c", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "OkBloomer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrew DalPino", + "email": "support@andrewdalpino.com", + "homepage": "https://github.com/andrewdalpino", + "role": "Lead Developer" + } + ], + "description": "An autoscaling Bloom filter with ultra-low memory usage for PHP.", + "keywords": [ + "Bloom Filter", + "layered bloom filter", + "scalable bloom filter" + ], + "support": { + "docs": "https://github.com/andrewdalpino/OkBloomer/README.md", + "email": "support@andrewdalpino.com", + "issues": "https://github.com/andrewdalpino/OkBloomer/issues", + "source": "https://github.com/andrewdalpino/OkBloomer" + }, + "funding": [ + { + "url": "https://github.com/sponsors/andrewdalpino", + "type": "github" + } + ], + "time": "2022-01-24T03:41:23+00:00" + }, { "name": "defuse/php-encryption", "version": "v2.4.0", @@ -743,16 +1299,16 @@ }, { "name": "doctrine/inflector", - "version": "2.0.8", + "version": "2.0.9", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff" + "reference": "2930cd5ef353871c821d5c43ed030d39ac8cfe65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/f9301a5b2fb1216b2b08f02ba04dc45423db6bff", - "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/2930cd5ef353871c821d5c43ed030d39ac8cfe65", + "reference": "2930cd5ef353871c821d5c43ed030d39ac8cfe65", "shasum": "" }, "require": { @@ -814,7 +1370,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.8" + "source": "https://github.com/doctrine/inflector/tree/2.0.9" }, "funding": [ { @@ -830,7 +1386,7 @@ "type": "tidelift" } ], - "time": "2023-06-16T13:40:37+00:00" + "time": "2024-01-15T18:05:13+00:00" }, { "name": "doctrine/instantiator", @@ -3636,8 +4192,222 @@ "type": "library", "autoload": { "files": [ - "src/getallheaders.php" - ] + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "rubix/ml", + "version": "2.4.0", + "source": { + "type": "git", + "url": "https://github.com/RubixML/ML.git", + "reference": "e804cdc318801545307ed4a0f3a835fcf22f5864" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/RubixML/ML/zipball/e804cdc318801545307ed4a0f3a835fcf22f5864", + "reference": "e804cdc318801545307ed4a0f3a835fcf22f5864", + "shasum": "" + }, + "require": { + "amphp/parallel": "^1.3", + "andrewdalpino/okbloomer": "^1.0", + "ext-json": "*", + "php": ">=7.4", + "psr/log": "^1.1|^2.0|^3.0", + "rubix/tensor": "^3.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.17", + "symfony/polyfill-php82": "^1.27", + "symfony/polyfill-php83": "^1.27", + "wamania/php-stemmer": "^3.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.0" + }, + "suggest": { + "ext-gd": "For image support", + "ext-mbstring": "For fast multibyte string manipulation", + "ext-svm": "For Support Vector Machine engine (libsvm)", + "ext-tensor": "For fast Matrix/Vector computing" + }, + "type": "library", + "autoload": { + "files": [ + "src/constants.php", + "src/functions.php" + ], + "psr-4": { + "Rubix\\ML\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrew DalPino", + "homepage": "https://github.com/andrewdalpino" + }, + { + "name": "Contributors", + "homepage": "https://github.com/RubixML/ML/graphs/contributors" + } + ], + "description": "A high-level machine learning and deep learning library for the PHP language.", + "homepage": "https://rubixml.com", + "keywords": [ + "Algorithm", + "Deep learning", + "Linear regression", + "Neural network", + "Rubix", + "TF-IDF", + "adaboost", + "ai", + "analytics", + "anomaly detection", + "artificial intelligence", + "cart", + "classification", + "classifier", + "clustering", + "cross validation", + "data mining", + "data science", + "dataset", + "dbscan", + "dimensionality reduction", + "ensemble", + "estimator", + "etl", + "feature extraction", + "feature importance", + "feature selection", + "gaussian mixture", + "gbm", + "gmm", + "gradient boost", + "grid search", + "image recognition", + "imputation", + "inference", + "isolation forest", + "k-means", + "k-nearest neighbors", + "kmeans", + "knn", + "local outlier factor", + "loda", + "lof", + "logistic regression", + "machine learning", + "manifold learning", + "mean shift", + "ml", + "mlp", + "multilayer perceptron", + "naive bayes", + "natural language processing", + "nearest neighbors", + "nlp", + "outlier detection", + "php", + "php ai", + "php machine learning", + "php ml", + "prediction", + "predictive modeling", + "random forest", + "ranking", + "recommendation", + "regression", + "regressor", + "ridge", + "rubix ml", + "rubixml", + "softmax", + "supervised learning", + "support vector machine", + "svm", + "t-sne", + "text mining", + "tf idf", + "tsne", + "unsupervised learning" + ], + "support": { + "chat": "https://t.me/RubixML", + "docs": "https://docs.rubixml.com", + "issues": "https://github.com/RubixML/ML/issues", + "source": "https://github.com/RubixML/ML" + }, + "funding": [ + { + "url": "https://github.com/sponsors/andrewdalpino", + "type": "github" + } + ], + "time": "2023-05-26T16:49:18+00:00" + }, + { + "name": "rubix/tensor", + "version": "3.0.4", + "source": { + "type": "git", + "url": "https://github.com/RubixML/Tensor.git", + "reference": "e1537119c5f1f87eb506852df3b5b7c7e34c452a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/RubixML/Tensor/zipball/e1537119c5f1f87eb506852df3b5b7c7e34c452a", + "reference": "e1537119c5f1f87eb506852df3b5b7c7e34c452a", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phalcon/zephir": "^0.17", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/constants.php" + ], + "psr-4": { + "Tensor\\": "src/", + "Zephir\\Optimizers\\FunctionCall\\": "optimizers/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3645,16 +4415,66 @@ ], "authors": [ { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" + "name": "Andrew DalPino", + "email": "support@andrewdalpino.com", + "homepage": "https://github.com/andrewdalpino", + "role": "Project Lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/RubixML/Tensor/graphs/contributors" } ], - "description": "A polyfill for getallheaders.", + "description": "A library and extension that provides objects for scientific computing in PHP.", + "homepage": "https://github.com/RubixML/Tensor", + "keywords": [ + "1d convolution", + "2d convolution", + "arithmetic", + "blas", + "computation", + "computing", + "convolution", + "decomposition", + "dot product", + "eigendecomposition", + "eigenvalue", + "eigenvector", + "engineering", + "extension", + "lapack", + "linear algebra", + "math", + "matmul", + "matrix", + "matrix multiplication", + "multithreaded", + "php", + "php extension", + "pseudoinverse", + "scientific computing", + "signal processing", + "singular value decomposition", + "statistics", + "svd", + "tensor", + "trigonometry", + "vector", + "vector norm" + ], "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" + "chat": "https://t.me/RubixML", + "email": "support@andrewdalpino.com", + "issues": "https://github.com/RubixML/Tensor/issues", + "source": "https://github.com/RubixML/Tensor" }, - "time": "2019-03-08T08:55:37+00:00" + "funding": [ + { + "url": "https://github.com/andrewdalpino", + "type": "github" + } + ], + "time": "2023-09-20T05:03:49+00:00" }, { "name": "symfony/cache", @@ -4353,6 +5173,89 @@ ], "time": "2023-01-26T09:26:14+00:00" }, + { + "name": "symfony/polyfill-iconv", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-iconv.git", + "reference": "6de50471469b8c9afc38164452ab2b6170ee71c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/6de50471469b8c9afc38164452ab2b6170ee71c1", + "reference": "6de50471469b8c9afc38164452ab2b6170ee71c1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-iconv": "*" + }, + "suggest": { + "ext-iconv": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Iconv\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Iconv extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "iconv", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, { "name": "symfony/polyfill-intl-grapheme", "version": "v1.28.0", @@ -4371,7 +5274,175 @@ "php": ">=7.1" }, "suggest": { - "ext-intl": "For best performance" + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "42292d99c55abe617799667f454222c54c60e229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" }, "type": "library", "extra": { @@ -4388,7 +5459,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + "Symfony\\Polyfill\\Mbstring\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -4405,18 +5476,17 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's grapheme_* functions", + "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "grapheme", - "intl", + "mbstring", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" }, "funding": [ { @@ -4432,28 +5502,25 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2023-07-28T09:04:16+00:00" }, { - "name": "symfony/polyfill-intl-normalizer", + "name": "symfony/polyfill-php72", "version": "v1.28.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", - "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179", + "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179", "shasum": "" }, "require": { "php": ">=7.1" }, - "suggest": { - "ext-intl": "For best performance" - }, "type": "library", "extra": { "branch-alias": { @@ -4469,11 +5536,8 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" - ] + "Symfony\\Polyfill\\Php72\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4489,18 +5553,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "intl", - "normalizer", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.28.0" }, "funding": [ { @@ -4519,28 +5581,22 @@ "time": "2023-01-26T09:26:14+00:00" }, { - "name": "symfony/polyfill-mbstring", + "name": "symfony/polyfill-php80", "version": "v1.28.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "42292d99c55abe617799667f454222c54c60e229" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", - "reference": "42292d99c55abe617799667f454222c54c60e229", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", "shasum": "" }, "require": { "php": ">=7.1" }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, "type": "library", "extra": { "branch-alias": { @@ -4556,14 +5612,21 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -4573,17 +5636,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "mbstring", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" }, "funding": [ { @@ -4599,20 +5661,20 @@ "type": "tidelift" } ], - "time": "2023-07-28T09:04:16+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { - "name": "symfony/polyfill-php72", + "name": "symfony/polyfill-php82", "version": "v1.28.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179" + "url": "https://github.com/symfony/polyfill-php82.git", + "reference": "7716bea9c86776fb3362d6b52fe1fc9471056a49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179", - "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179", + "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/7716bea9c86776fb3362d6b52fe1fc9471056a49", + "reference": "7716bea9c86776fb3362d6b52fe1fc9471056a49", "shasum": "" }, "require": { @@ -4633,8 +5695,11 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - } + "Symfony\\Polyfill\\Php82\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4650,7 +5715,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.2+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -4659,7 +5724,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-php82/tree/v1.28.0" }, "funding": [ { @@ -4675,24 +5740,25 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2023-08-25T17:27:25+00:00" }, { - "name": "symfony/polyfill-php80", + "name": "symfony/polyfill-php83", "version": "v1.28.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11", + "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.1", + "symfony/polyfill-php80": "^1.14" }, "type": "library", "extra": { @@ -4709,7 +5775,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" + "Symfony\\Polyfill\\Php83\\": "" }, "classmap": [ "Resources/stubs" @@ -4720,10 +5786,6 @@ "MIT" ], "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -4733,7 +5795,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -4742,7 +5804,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.28.0" }, "funding": [ { @@ -4758,7 +5820,7 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2023-08-16T06:22:46+00:00" }, { "name": "symfony/routing", @@ -5353,6 +6415,232 @@ ], "time": "2023-11-21T18:54:41+00:00" }, + { + "name": "voku/portable-ascii", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b56450eed252f6801410d810c8e1727224ae0743" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", + "reference": "b56450eed252f6801410d810c8e1727224ae0743", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "http://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.1" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2022-03-08T17:03:00+00:00" + }, + { + "name": "voku/portable-utf8", + "version": "6.0.13", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-utf8.git", + "reference": "b8ce36bf26593e5c2e81b1850ef0ffb299d2043f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-utf8/zipball/b8ce36bf26593e5c2e81b1850ef0ffb299d2043f", + "reference": "b8ce36bf26593e5c2e81b1850ef0ffb299d2043f", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "symfony/polyfill-iconv": "~1.0", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php72": "~1.0", + "voku/portable-ascii": "~2.0.0" + }, + "require-dev": { + "phpstan/phpstan": "1.9.*@dev", + "phpstan/phpstan-strict-rules": "1.4.*@dev", + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0", + "thecodingmachine/phpstan-strict-rules": "1.0.*@dev", + "voku/phpstan-rules": "3.1.*@dev" + }, + "suggest": { + "ext-ctype": "Use Ctype for e.g. hexadecimal digit detection", + "ext-fileinfo": "Use Fileinfo for better binary file detection", + "ext-iconv": "Use iconv for best performance", + "ext-intl": "Use Intl for best performance", + "ext-json": "Use JSON for string detection", + "ext-mbstring": "Use Mbstring for best performance" + }, + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "(Apache-2.0 or GPL-2.0)" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Hamid Sarfraz", + "homepage": "http://pageconfig.com/" + }, + { + "name": "Lars Moelleken", + "homepage": "http://www.moelleken.org/" + } + ], + "description": "Portable UTF-8 library - performance optimized (unicode) string functions for php.", + "homepage": "https://github.com/voku/portable-utf8", + "keywords": [ + "UTF", + "clean", + "php", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "issues": "https://github.com/voku/portable-utf8/issues", + "source": "https://github.com/voku/portable-utf8/tree/6.0.13" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-utf8", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-utf8", + "type": "tidelift" + } + ], + "time": "2023-03-08T08:35:38+00:00" + }, + { + "name": "wamania/php-stemmer", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/wamania/php-stemmer.git", + "reference": "8ea32b6fa27d6888587fe860b64a8763525c5a66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wamania/php-stemmer/zipball/8ea32b6fa27d6888587fe860b64a8763525c5a66", + "reference": "8ea32b6fa27d6888587fe860b64a8763525c5a66", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "voku/portable-utf8": "^5.4|^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Wamania\\Snowball\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Wamania", + "homepage": "http://wamania.com" + } + ], + "description": "Native PHP Stemmer", + "keywords": [ + "php", + "porter", + "stemmer" + ], + "support": { + "issues": "https://github.com/wamania/php-stemmer/issues", + "source": "https://github.com/wamania/php-stemmer/tree/v3.0.1" + }, + "time": "2023-05-11T10:50:27+00:00" + }, { "name": "webmozart/assert", "version": "1.11.0", @@ -5682,25 +6970,27 @@ }, { "name": "nikic/php-parser", - "version": "v4.18.0", + "version": "v5.0.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999" + "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4a21235f7e56e713259a6f76bf4b5ea08502b9dc", + "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, "bin": [ "bin/php-parse" @@ -5708,7 +6998,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -5732,9 +7022,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.0" }, - "time": "2023-12-10T21:03:43+00:00" + "time": "2024-01-07T17:17:35+00:00" }, { "name": "phar-io/manifest", @@ -6170,16 +7460,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.5", + "version": "10.5.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "ed21115d505b4b4f7dc7b5651464e19a2c7f7856" + "reference": "e5c5b397a95cb0db013270a985726fcae93e61b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ed21115d505b4b4f7dc7b5651464e19a2c7f7856", - "reference": "ed21115d505b4b4f7dc7b5651464e19a2c7f7856", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e5c5b397a95cb0db013270a985726fcae93e61b8", + "reference": "e5c5b397a95cb0db013270a985726fcae93e61b8", "shasum": "" }, "require": { @@ -6251,7 +7541,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.5" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.7" }, "funding": [ { @@ -6267,7 +7557,7 @@ "type": "tidelift" } ], - "time": "2023-12-27T15:13:52+00:00" + "time": "2024-01-14T16:40:30+00:00" }, { "name": "sebastian/cli-parser", diff --git a/config.ini.example b/config.ini.example index 8c4c6783..cdb398fb 100644 --- a/config.ini.example +++ b/config.ini.example @@ -19,6 +19,10 @@ default[pool_size] = 8 background_color = "#ffffff" theme_color = "#ffffff" +[ollama] +host = 127.0.0.1 +port = 11434 + [redis] default[db_index] = 0 default[host] = 127.0.0.1 diff --git a/src/Command/OllamaCompletion.php b/src/Command/OllamaCompletion.php new file mode 100644 index 00000000..bc80c164 --- /dev/null +++ b/src/Command/OllamaCompletion.php @@ -0,0 +1,64 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance\Command; + +use Distantmagic\Resonance\Attribute\ConsoleCommand; +use Distantmagic\Resonance\Command; +use Distantmagic\Resonance\CoroutineCommand; +use Distantmagic\Resonance\OllamaClient; +use Distantmagic\Resonance\OllamaCompletionRequest; +use Distantmagic\Resonance\SwooleConfiguration; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +#[ConsoleCommand( + name: 'ollama:completion', + description: 'Generate LLM completion' +)] +final class OllamaCompletion extends CoroutineCommand +{ + public function __construct( + private OllamaClient $ollamaClient, + SwooleConfiguration $swooleConfiguration, + ) { + parent::__construct($swooleConfiguration); + } + + protected function configure(): void + { + $this->addArgument('prompt', InputArgument::REQUIRED); + $this->addOption( + default: 'mistral', + mode: InputOption::VALUE_REQUIRED, + name: 'model', + ); + } + + protected function executeInCoroutine(InputInterface $input, OutputInterface $output): int + { + /** + * @var string $model + */ + $model = $input->getOption('model'); + + /** + * @var string $prompt + */ + $prompt = $input->getArgument('prompt'); + + $completionRequest = new OllamaCompletionRequest( + model: $model, + prompt: $prompt, + ); + + foreach ($this->ollamaClient->generateCompletion($completionRequest) as $chunk) { + $output->write($chunk); + } + + return Command::SUCCESS; + } +} diff --git a/src/Command/OllamaEmbedding.php b/src/Command/OllamaEmbedding.php new file mode 100644 index 00000000..9ec87ea6 --- /dev/null +++ b/src/Command/OllamaEmbedding.php @@ -0,0 +1,73 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance\Command; + +use Distantmagic\Resonance\Attribute\ConsoleCommand; +use Distantmagic\Resonance\Command; +use Distantmagic\Resonance\CoroutineCommand; +use Distantmagic\Resonance\JsonSerializer; +use Distantmagic\Resonance\OllamaClient; +use Distantmagic\Resonance\OllamaEmbeddingRequest; +use Distantmagic\Resonance\SwooleConfiguration; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +#[ConsoleCommand( + name: 'ollama:embedding', + description: 'Generate LLM embedding' +)] +final class OllamaEmbedding extends CoroutineCommand +{ + public function __construct( + private JsonSerializer $jsonSerializer, + private OllamaClient $ollamaClient, + SwooleConfiguration $swooleConfiguration, + ) { + parent::__construct($swooleConfiguration); + } + + protected function configure(): void + { + $this->addArgument('prompt', InputArgument::REQUIRED); + $this->addOption( + default: 'mistral', + mode: InputOption::VALUE_REQUIRED, + name: 'model', + ); + } + + protected function executeInCoroutine(InputInterface $input, OutputInterface $output): int + { + /** + * @var string $model + */ + $model = $input->getOption('model'); + + /** + * @var string $prompt + */ + $prompt = $input->getArgument('prompt'); + + $embeddingRequest = new OllamaEmbeddingRequest( + model: $model, + prompt: $prompt, + ); + + $embeddingResponse = $this + ->ollamaClient + ->generateEmbedding($embeddingRequest) + ; + + $output->writeln( + $this + ->jsonSerializer + ->serialize($embeddingResponse) + ); + + return Command::SUCCESS; + } +} diff --git a/src/CurlException.php b/src/CurlException.php new file mode 100644 index 00000000..15e1069d --- /dev/null +++ b/src/CurlException.php @@ -0,0 +1,20 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use CurlHandle; +use RuntimeException; + +class CurlException extends RuntimeException +{ + public function __construct(CurlHandle $ch) + { + parent::__construct(sprintf( + 'curl request failed because of error: (%d)"%s"', + curl_errno($ch), + curl_error($ch), + )); + } +} diff --git a/src/EsbuildMetaBuilder.php b/src/EsbuildMetaBuilder.php index 3200b85b..32366400 100644 --- a/src/EsbuildMetaBuilder.php +++ b/src/EsbuildMetaBuilder.php @@ -19,7 +19,7 @@ readonly class EsbuildMetaBuilder */ private Map $esbuildMetaCache; - public function __construct() + public function __construct(private JsonSerializer $jsonSerializer) { $this->esbuildMetaCache = new Map(); } @@ -148,10 +148,10 @@ readonly class EsbuildMetaBuilder private function getEsbuildMetaDecoded(string $esbuildMetafile): object { - $ret = json_decode( - json: $this->getEsbuildMetaContents($esbuildMetafile), - flags: JSON_THROW_ON_ERROR, - ); + $ret = $this + ->jsonSerializer + ->unserialize($this->getEsbuildMetaContents($esbuildMetafile)) + ; if (!is_object($ret)) { throw new LogicException('Expected manifest to be a JSON object.'); diff --git a/src/JsonSerializer.php b/src/JsonSerializer.php index 21011952..7fb69fa9 100644 --- a/src/JsonSerializer.php +++ b/src/JsonSerializer.php @@ -20,4 +20,12 @@ readonly class JsonSerializer : JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES, ); } + + public function unserialize(string $data): mixed + { + return json_decode( + json: $data, + flags: JSON_THROW_ON_ERROR, + ); + } } diff --git a/src/OllamaClient.php b/src/OllamaClient.php new file mode 100644 index 00000000..58cc53f4 --- /dev/null +++ b/src/OllamaClient.php @@ -0,0 +1,123 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use CurlHandle; +use Distantmagic\Resonance\Attribute\Singleton; +use Generator; +use RuntimeException; +use Swoole\Coroutine\Channel; + +#[Singleton] +readonly class OllamaClient +{ + private CurlHandle $ch; + + public function __construct( + private JsonSerializer $jsonSerializer, + private OllamaLinkBuilder $ollamaLinkBuilder, + ) { + $this->ch = curl_init(); + + curl_setopt($this->ch, CURLOPT_POST, true); + curl_setopt($this->ch, CURLOPT_HTTPHEADER, [ + 'Content-Type: application/json', + ]); + } + + public function __destruct() + { + curl_close($this->ch); + } + + /** + * @return Generator<string> + */ + public function generateCompletion(OllamaCompletionRequest $request): Generator + { + $channel = new Channel(1); + $data = json_encode($request); + + $cid = go(function () use ($channel, $data) { + try { + curl_setopt($this->ch, CURLOPT_URL, $this->ollamaLinkBuilder->build('/api/generate')); + curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, false); + curl_setopt($this->ch, CURLOPT_WRITEFUNCTION, function (CurlHandle $ch, string $data) use ($channel) { + if (!empty($data)) { + $channel->push( + $this + ->jsonSerializer + ->unserialize($data) + ); + } + + return strlen($data); + }); + + if (!curl_exec($this->ch)) { + throw new CurlException($this->ch); + } + + $this->assertStatusCode(200); + } finally { + curl_setopt($this->ch, CURLOPT_WRITEFUNCTION, null); + + $channel->close(); + } + }); + + if (!is_int($cid)) { + throw new RuntimeException('Unable to start a coroutine'); + } + + foreach (new SwooleChannelIterator($channel) as $data) { + if ($data) { + yield $data->response; + } + } + } + + public function generateEmbedding(OllamaEmbeddingRequest $request): OllamaEmbeddingResponse + { + $data = json_encode($request); + + curl_setopt($this->ch, CURLOPT_URL, $this->ollamaLinkBuilder->build('/api/embeddings')); + curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true); + + $responseContent = curl_exec($this->ch); + + if (false === $responseContent) { + throw new CurlException($this->ch); + } + + $this->assertStatusCode(200); + + $data = $this + ->jsonSerializer + ->unserialize($responseContent) + ; + + return new OllamaEmbeddingResponse($data->embedding); + } + + private function assertStatusCode(int $expectedStatusCode): void + { + /** + * @var int $statusCode + */ + $statusCode = curl_getinfo($this->ch, CURLINFO_RESPONSE_CODE); + + if ($expectedStatusCode === $statusCode) { + return; + } + + throw new RuntimeException(sprintf( + 'curl request finished with unexpected status code: "%s"', + $statusCode, + )); + } +} diff --git a/src/OllamaCompletionRequest.php b/src/OllamaCompletionRequest.php new file mode 100644 index 00000000..1215d4fe --- /dev/null +++ b/src/OllamaCompletionRequest.php @@ -0,0 +1,32 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use JsonSerializable; + +readonly class OllamaCompletionRequest implements JsonSerializable +{ + public function __construct( + public string $model, + public string $prompt, + public OllamaRequestOptions $options = new OllamaRequestOptions(), + ) {} + + public function jsonSerialize(): array + { + return [ + 'model' => $this->model, + 'options' => $this->options, + 'prompt' => sprintf( + '%s%s%s', + $this->options->stopDelimiter->instructions, + $this->prompt, + $this->options->stopDelimiter->system, + ), + 'raw' => true, + 'stream' => true, + ]; + } +} diff --git a/src/OllamaConfiguration.php b/src/OllamaConfiguration.php new file mode 100644 index 00000000..9796d667 --- /dev/null +++ b/src/OllamaConfiguration.php @@ -0,0 +1,14 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +readonly class OllamaConfiguration +{ + public function __construct( + public string $host, + public int $port, + public string $scheme, + ) {} +} diff --git a/src/OllamaEmbeddingRequest.php b/src/OllamaEmbeddingRequest.php new file mode 100644 index 00000000..cdfa7b47 --- /dev/null +++ b/src/OllamaEmbeddingRequest.php @@ -0,0 +1,27 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use JsonSerializable; + +readonly class OllamaEmbeddingRequest implements JsonSerializable +{ + public function __construct( + public string $model, + public string $prompt, + public OllamaRequestOptions $options = new OllamaRequestOptions(), + ) {} + + public function jsonSerialize(): array + { + return [ + 'model' => $this->model, + 'options' => $this->options, + 'prompt' => $this->prompt, + 'raw' => true, + 'stream' => true, + ]; + } +} diff --git a/src/OllamaEmbeddingResponse.php b/src/OllamaEmbeddingResponse.php new file mode 100644 index 00000000..955bc499 --- /dev/null +++ b/src/OllamaEmbeddingResponse.php @@ -0,0 +1,20 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use JsonSerializable; + +readonly class OllamaEmbeddingResponse implements JsonSerializable +{ + /** + * @param array<float> $embedding + */ + public function __construct(public array $embedding) {} + + public function jsonSerialize(): array + { + return $this->embedding; + } +} diff --git a/src/OllamaLinkBuilder.php b/src/OllamaLinkBuilder.php new file mode 100644 index 00000000..281a2de6 --- /dev/null +++ b/src/OllamaLinkBuilder.php @@ -0,0 +1,26 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use Distantmagic\Resonance\Attribute\Singleton; + +#[Singleton] +readonly class OllamaLinkBuilder +{ + public function __construct( + private OllamaConfiguration $ollamaConfiguration, + ) {} + + public function build(string $path): string + { + return sprintf( + '%s://%s:%d%s', + $this->ollamaConfiguration->scheme, + $this->ollamaConfiguration->host, + $this->ollamaConfiguration->port, + $path, + ); + } +} diff --git a/src/OllamaRequestOptions.php b/src/OllamaRequestOptions.php new file mode 100644 index 00000000..3e5c8b42 --- /dev/null +++ b/src/OllamaRequestOptions.php @@ -0,0 +1,28 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use JsonSerializable; + +readonly class OllamaRequestOptions implements JsonSerializable +{ + public function __construct( + public float $temperature = 0.8, + public OllamaRequestStopDelimiter $stopDelimiter = new OllamaRequestStopDelimiter(), + ) {} + + public function jsonSerialize(): array + { + $ret = []; + + if (isset($this->stopDelimiter)) { + $ret['stop'] = $this->stopDelimiter; + } + + $ret['temperature'] = $this->temperature; + + return $ret; + } +} diff --git a/src/OllamaRequestStopDelimiter.php b/src/OllamaRequestStopDelimiter.php new file mode 100644 index 00000000..750b3f0b --- /dev/null +++ b/src/OllamaRequestStopDelimiter.php @@ -0,0 +1,23 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use JsonSerializable; + +readonly class OllamaRequestStopDelimiter implements JsonSerializable +{ + public function __construct( + public string $instructions = '[INST]', + public string $system = '[SYS]', + ) {} + + public function jsonSerialize(): array + { + return [ + $this->instructions, + $this->system, + ]; + } +} diff --git a/src/SingletonProvider/ConfigurationProvider/OllamaConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/OllamaConfigurationProvider.php new file mode 100644 index 00000000..086d16d6 --- /dev/null +++ b/src/SingletonProvider/ConfigurationProvider/OllamaConfigurationProvider.php @@ -0,0 +1,59 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider; + +use Distantmagic\Resonance\Attribute\Singleton; +use Distantmagic\Resonance\JsonSchema; +use Distantmagic\Resonance\OllamaConfiguration; +use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider; + +/** + * @template-extends ConfigurationProvider<OllamaConfiguration, object{ + * host: string, + * port: int, + * scheme: string, + * }> + */ +#[Singleton(provides: OllamaConfiguration::class)] +final readonly class OllamaConfigurationProvider extends ConfigurationProvider +{ + protected function getConfigurationKey(): string + { + return 'ollama'; + } + + protected function makeSchema(): JsonSchema + { + return new JsonSchema([ + 'type' => 'object', + 'properties' => [ + 'host' => [ + 'type' => 'string', + 'minLength' => 1, + ], + 'port' => [ + 'type' => 'integer', + 'minimum' => 1, + 'maximum' => 65535, + ], + 'scheme' => [ + 'type' => 'string', + 'enum' => ['http', 'https'], + 'default' => 'http', + ], + ], + 'required' => ['host', 'port'], + ]); + } + + protected function provideConfiguration($validatedData): OllamaConfiguration + { + return new OllamaConfiguration( + host: $validatedData->host, + port: $validatedData->port, + scheme: $validatedData->scheme, + ); + } +} diff --git a/src/SwooleChannelIterator.php b/src/SwooleChannelIterator.php new file mode 100644 index 00000000..6794931c --- /dev/null +++ b/src/SwooleChannelIterator.php @@ -0,0 +1,45 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use Generator; +use IteratorAggregate; +use RuntimeException; +use Swoole\Coroutine\Channel; + +/** + * @template TData + * + * @template-implements IteratorAggregate<TData> + */ +readonly class SwooleChannelIterator implements IteratorAggregate +{ + public function __construct(private Channel $channel) {} + + /** + * @return Generator<TData> + */ + public function getIterator(): Generator + { + do { + $data = $this->channel->pop(); + + if (false === $data) { + switch ($this->channel->errCode) { + case SWOOLE_CHANNEL_CLOSED: + return; + case SWOOLE_CHANNEL_OK: + yield $data; + + break; + case SWOOLE_CHANNEL_TIMEOUT: + throw new RuntimeException('Swoole channel timed out'); + } + } else { + yield $data; + } + } while (true); + } +} diff --git a/src/WebSocketProtocolController/RPCProtocolController.php b/src/WebSocketProtocolController/RPCProtocolController.php index 8ce8849f..b9545ae9 100644 --- a/src/WebSocketProtocolController/RPCProtocolController.php +++ b/src/WebSocketProtocolController/RPCProtocolController.php @@ -11,6 +11,7 @@ use Distantmagic\Resonance\CSRFManager; use Distantmagic\Resonance\Feature; use Distantmagic\Resonance\Gatekeeper; use Distantmagic\Resonance\InputValidator\RPCMessageValidator; +use Distantmagic\Resonance\JsonSerializer; use Distantmagic\Resonance\SingletonCollection; use Distantmagic\Resonance\SiteAction; use Distantmagic\Resonance\WebSocketAuthResolution; @@ -45,6 +46,7 @@ final readonly class RPCProtocolController extends WebSocketProtocolController private CSRFManager $csrfManager, private AuthenticatedUserProvider $authenticatedUserProvider, private Gatekeeper $gatekeeper, + private JsonSerializer $jsonSerializer, private LoggerInterface $logger, private RPCMessageValidator $rpcMessageValidator, private WebSocketRPCResponderAggregate $webSocketRPCResponderAggregate, @@ -87,10 +89,7 @@ final readonly class RPCProtocolController extends WebSocketProtocolController /** * @var mixed $decodedRpcMessage explicitly mixed for typechecks */ - $decodedRpcMessage = json_decode( - json: $frame->data, - flags: JSON_THROW_ON_ERROR, - ); + $decodedRpcMessage = $this->jsonSerializer->unserialize($frame->data); $this->onJsonMessage($server, $frame, $decodedRpcMessage); } catch (JsonException $exception) { -- GitLab