diff --git a/composer.json b/composer.json index 7a5d9ed0e57c18e7e453705cd35ad97fba2dac89..5e96603c5032d3093abaea88d84e8821ee7343de 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,7 @@ "dragonmantank/cron-expression": "^3.3", "ezyang/htmlpurifier": "^4.16", "grpc/grpc": "^1.57", + "hyperf/coroutine": "^3.1", "hyperf/grpc-client": "^3.1", "league/commonmark": "^2.4", "league/oauth2-client": "^2.7", @@ -60,7 +61,8 @@ "require-dev": { "phpunit/phpunit": "^11.0", "swoole/ide-helper": "^5.1", - "symfony/var-dumper": "^7.0" + "symfony/var-dumper": "^7.0", + "mockery/mockery": "^1.6" }, "suggest": { "ext-ds": "For better memory management", diff --git a/composer.lock b/composer.lock index 2de4e9571e7e0415e503196407c93fd849d6c752..0cafaf8810ae2e031c6f9e226b430e60cef595b3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6cc5c7a01d9bb04a890e15a3e326d795", + "content-hash": "2e42788f572c165ae18374fbd867a9b7", "packages": [ { "name": "brick/math", @@ -1841,16 +1841,16 @@ }, { "name": "hyperf/code-parser", - "version": "v3.1.4", + "version": "v3.1.15", "source": { "type": "git", "url": "https://github.com/hyperf/code-parser.git", - "reference": "e36ac337cf7852aaa817db61ade0941d8826f0d8" + "reference": "820e8e6680f0d04e4187a3037a2a3eaf7141913d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyperf/code-parser/zipball/e36ac337cf7852aaa817db61ade0941d8826f0d8", - "reference": "e36ac337cf7852aaa817db61ade0941d8826f0d8", + "url": "https://api.github.com/repos/hyperf/code-parser/zipball/820e8e6680f0d04e4187a3037a2a3eaf7141913d", + "reference": "820e8e6680f0d04e4187a3037a2a3eaf7141913d", "shasum": "" }, "require": { @@ -1902,20 +1902,20 @@ "type": "open_collective" } ], - "time": "2023-12-28T08:46:40+00:00" + "time": "2024-03-23T11:28:51+00:00" }, { "name": "hyperf/collection", - "version": "v3.1.7", + "version": "v3.1.15", "source": { "type": "git", "url": "https://github.com/hyperf/collection.git", - "reference": "40e25ca34f798e173a1f9a6871d56a6abae43dbb" + "reference": "d0ac957987a704c8b2a16de4333b81f1f56a724a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyperf/collection/zipball/40e25ca34f798e173a1f9a6871d56a6abae43dbb", - "reference": "40e25ca34f798e173a1f9a6871d56a6abae43dbb", + "url": "https://api.github.com/repos/hyperf/collection/zipball/d0ac957987a704c8b2a16de4333b81f1f56a724a", + "reference": "d0ac957987a704c8b2a16de4333b81f1f56a724a", "shasum": "" }, "require": { @@ -1965,20 +1965,20 @@ "type": "open_collective" } ], - "time": "2024-01-26T01:52:03+00:00" + "time": "2024-03-23T11:28:51+00:00" }, { "name": "hyperf/conditionable", - "version": "v3.1.0", + "version": "v3.1.15", "source": { "type": "git", "url": "https://github.com/hyperf/conditionable.git", - "reference": "18da1405ae39a775bd3fae8cec98841eaa22f013" + "reference": "2c1555891d136904b890ba6d812d9ff50ca13ae3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyperf/conditionable/zipball/18da1405ae39a775bd3fae8cec98841eaa22f013", - "reference": "18da1405ae39a775bd3fae8cec98841eaa22f013", + "url": "https://api.github.com/repos/hyperf/conditionable/zipball/2c1555891d136904b890ba6d812d9ff50ca13ae3", + "reference": "2c1555891d136904b890ba6d812d9ff50ca13ae3", "shasum": "" }, "require": { @@ -2023,20 +2023,20 @@ "type": "open_collective" } ], - "time": "2023-11-24T03:10:53+00:00" + "time": "2024-03-23T11:28:51+00:00" }, { "name": "hyperf/context", - "version": "v3.1.0", + "version": "v3.1.15", "source": { "type": "git", "url": "https://github.com/hyperf/context.git", - "reference": "8307d6c924ed67c7abd47874ec14f0e2e3e4b732" + "reference": "ad913fd50eb5f738c038e172c120bc6956c0da69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyperf/context/zipball/8307d6c924ed67c7abd47874ec14f0e2e3e4b732", - "reference": "8307d6c924ed67c7abd47874ec14f0e2e3e4b732", + "url": "https://api.github.com/repos/hyperf/context/zipball/ad913fd50eb5f738c038e172c120bc6956c0da69", + "reference": "ad913fd50eb5f738c038e172c120bc6956c0da69", "shasum": "" }, "require": { @@ -2085,20 +2085,20 @@ "type": "open_collective" } ], - "time": "2023-11-24T03:10:53+00:00" + "time": "2024-03-23T11:28:51+00:00" }, { "name": "hyperf/contract", - "version": "v3.1.2", + "version": "v3.1.15", "source": { "type": "git", "url": "https://github.com/hyperf/contract.git", - "reference": "f5379df6df65363d645506f373888372135ac0c6" + "reference": "9950abe963cc6b30c6d3506fa5b3adbd58cb1945" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyperf/contract/zipball/f5379df6df65363d645506f373888372135ac0c6", - "reference": "f5379df6df65363d645506f373888372135ac0c6", + "url": "https://api.github.com/repos/hyperf/contract/zipball/9950abe963cc6b30c6d3506fa5b3adbd58cb1945", + "reference": "9950abe963cc6b30c6d3506fa5b3adbd58cb1945", "shasum": "" }, "require": { @@ -2142,20 +2142,20 @@ "type": "open_collective" } ], - "time": "2023-12-11T03:14:01+00:00" + "time": "2024-03-23T11:28:51+00:00" }, { "name": "hyperf/coroutine", - "version": "v3.1.1", + "version": "v3.1.15", "source": { "type": "git", "url": "https://github.com/hyperf/coroutine.git", - "reference": "cd5bad67724c5c7a7ad749d8e9eb045470488d75" + "reference": "8f4c573a9457646db3e629dacabe064eebaf8cc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyperf/coroutine/zipball/cd5bad67724c5c7a7ad749d8e9eb045470488d75", - "reference": "cd5bad67724c5c7a7ad749d8e9eb045470488d75", + "url": "https://api.github.com/repos/hyperf/coroutine/zipball/8f4c573a9457646db3e629dacabe064eebaf8cc1", + "reference": "8f4c573a9457646db3e629dacabe064eebaf8cc1", "shasum": "" }, "require": { @@ -2206,7 +2206,7 @@ "type": "open_collective" } ], - "time": "2023-12-01T06:59:45+00:00" + "time": "2024-03-23T11:28:51+00:00" }, { "name": "hyperf/engine", @@ -2357,16 +2357,16 @@ }, { "name": "hyperf/event", - "version": "v3.1.0", + "version": "v3.1.15", "source": { "type": "git", "url": "https://github.com/hyperf/event.git", - "reference": "6eada5f74ce80786c567d5aed0361d51175217bb" + "reference": "8d008682c028e958197589e90232bb2a1d3c77d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyperf/event/zipball/6eada5f74ce80786c567d5aed0361d51175217bb", - "reference": "6eada5f74ce80786c567d5aed0361d51175217bb", + "url": "https://api.github.com/repos/hyperf/event/zipball/8d008682c028e958197589e90232bb2a1d3c77d9", + "reference": "8d008682c028e958197589e90232bb2a1d3c77d9", "shasum": "" }, "require": { @@ -2420,20 +2420,20 @@ "type": "open_collective" } ], - "time": "2023-11-24T03:10:53+00:00" + "time": "2024-03-23T11:28:51+00:00" }, { "name": "hyperf/grpc", - "version": "v3.1.11", + "version": "v3.1.15", "source": { "type": "git", "url": "https://github.com/hyperf/grpc.git", - "reference": "63d8c8aac9a9fd7e586aa1298d9928107e6e0e27" + "reference": "7b0424ce1b9af5016e2c8580d6de9eb9c6ec4bed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyperf/grpc/zipball/63d8c8aac9a9fd7e586aa1298d9928107e6e0e27", - "reference": "63d8c8aac9a9fd7e586aa1298d9928107e6e0e27", + "url": "https://api.github.com/repos/hyperf/grpc/zipball/7b0424ce1b9af5016e2c8580d6de9eb9c6ec4bed", + "reference": "7b0424ce1b9af5016e2c8580d6de9eb9c6ec4bed", "shasum": "" }, "require": { @@ -2483,20 +2483,20 @@ "type": "open_collective" } ], - "time": "2024-02-28T03:13:03+00:00" + "time": "2024-03-23T11:28:51+00:00" }, { "name": "hyperf/grpc-client", - "version": "v3.1.7", + "version": "v3.1.15", "source": { "type": "git", "url": "https://github.com/hyperf/grpc-client.git", - "reference": "ce4cbf105f5f890f43bdd1997b9d69d4d09f5549" + "reference": "4f8f1aeef090d7789020e87a058ed35dcf3fc127" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyperf/grpc-client/zipball/ce4cbf105f5f890f43bdd1997b9d69d4d09f5549", - "reference": "ce4cbf105f5f890f43bdd1997b9d69d4d09f5549", + "url": "https://api.github.com/repos/hyperf/grpc-client/zipball/4f8f1aeef090d7789020e87a058ed35dcf3fc127", + "reference": "4f8f1aeef090d7789020e87a058ed35dcf3fc127", "shasum": "" }, "require": { @@ -2555,20 +2555,20 @@ "type": "open_collective" } ], - "time": "2024-01-22T08:45:43+00:00" + "time": "2024-03-23T11:28:51+00:00" }, { "name": "hyperf/macroable", - "version": "v3.1.0", + "version": "v3.1.15", "source": { "type": "git", "url": "https://github.com/hyperf/macroable.git", - "reference": "de5b07be74d666f04ecef4ce5ee6ceb97d846cfa" + "reference": "8912b5de69d25451b8ca103e4e47f0935e81072b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyperf/macroable/zipball/de5b07be74d666f04ecef4ce5ee6ceb97d846cfa", - "reference": "de5b07be74d666f04ecef4ce5ee6ceb97d846cfa", + "url": "https://api.github.com/repos/hyperf/macroable/zipball/8912b5de69d25451b8ca103e4e47f0935e81072b", + "reference": "8912b5de69d25451b8ca103e4e47f0935e81072b", "shasum": "" }, "require": { @@ -2613,20 +2613,20 @@ "type": "open_collective" } ], - "time": "2023-11-24T03:10:53+00:00" + "time": "2024-03-23T11:28:51+00:00" }, { "name": "hyperf/stdlib", - "version": "v3.1.0", + "version": "v3.1.15", "source": { "type": "git", "url": "https://github.com/hyperf/stdlib.git", - "reference": "25b73da235551d0d71d9157324709abaea36c455" + "reference": "636fdc1f15d9357b4747fa649874725f2276b118" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyperf/stdlib/zipball/25b73da235551d0d71d9157324709abaea36c455", - "reference": "25b73da235551d0d71d9157324709abaea36c455", + "url": "https://api.github.com/repos/hyperf/stdlib/zipball/636fdc1f15d9357b4747fa649874725f2276b118", + "reference": "636fdc1f15d9357b4747fa649874725f2276b118", "shasum": "" }, "require": { @@ -2671,20 +2671,20 @@ "type": "open_collective" } ], - "time": "2023-11-24T03:10:53+00:00" + "time": "2024-03-23T11:28:51+00:00" }, { "name": "hyperf/stringable", - "version": "v3.1.13", + "version": "v3.1.15", "source": { "type": "git", "url": "https://github.com/hyperf/stringable.git", - "reference": "b79f7204c35874a4ea0e917239b74fd230c93e1f" + "reference": "6cbd6f220d833c3f2c28c8263dccffee48021dcb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyperf/stringable/zipball/b79f7204c35874a4ea0e917239b74fd230c93e1f", - "reference": "b79f7204c35874a4ea0e917239b74fd230c93e1f", + "url": "https://api.github.com/repos/hyperf/stringable/zipball/6cbd6f220d833c3f2c28c8263dccffee48021dcb", + "reference": "6cbd6f220d833c3f2c28c8263dccffee48021dcb", "shasum": "" }, "require": { @@ -2742,20 +2742,20 @@ "type": "open_collective" } ], - "time": "2024-03-10T09:19:40+00:00" + "time": "2024-03-23T11:28:51+00:00" }, { "name": "hyperf/support", - "version": "v3.1.13", + "version": "v3.1.15", "source": { "type": "git", "url": "https://github.com/hyperf/support.git", - "reference": "cd5d284d83d0c031be38e3c5d07f6b96b837a42f" + "reference": "3fb5c6c5a4f795cb0304a032f6f5b85f62a5f872" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyperf/support/zipball/cd5d284d83d0c031be38e3c5d07f6b96b837a42f", - "reference": "cd5d284d83d0c031be38e3c5d07f6b96b837a42f", + "url": "https://api.github.com/repos/hyperf/support/zipball/3fb5c6c5a4f795cb0304a032f6f5b85f62a5f872", + "reference": "3fb5c6c5a4f795cb0304a032f6f5b85f62a5f872", "shasum": "" }, "require": { @@ -2812,20 +2812,20 @@ "type": "open_collective" } ], - "time": "2024-03-13T08:47:07+00:00" + "time": "2024-03-23T11:28:51+00:00" }, { "name": "hyperf/tappable", - "version": "v3.1.0", + "version": "v3.1.15", "source": { "type": "git", "url": "https://github.com/hyperf/tappable.git", - "reference": "f640e37006dad09ca6f2b9a4cf047907aaebf002" + "reference": "69f22bbc8ecb5b930cc95a49ae9bf0ca0efbfdf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyperf/tappable/zipball/f640e37006dad09ca6f2b9a4cf047907aaebf002", - "reference": "f640e37006dad09ca6f2b9a4cf047907aaebf002", + "url": "https://api.github.com/repos/hyperf/tappable/zipball/69f22bbc8ecb5b930cc95a49ae9bf0ca0efbfdf1", + "reference": "69f22bbc8ecb5b930cc95a49ae9bf0ca0efbfdf1", "shasum": "" }, "require": { @@ -2873,7 +2873,7 @@ "type": "open_collective" } ], - "time": "2023-11-24T03:10:53+00:00" + "time": "2024-03-23T11:28:51+00:00" }, { "name": "jean85/pretty-package-versions", @@ -7667,6 +7667,140 @@ } ], "packages-dev": [ + { + "name": "hamcrest/hamcrest-php", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "shasum": "" + }, + "require": { + "php": "^5.3|^7.0|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, + "time": "2020-07-09T08:09:16+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.11", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "81a161d0b135df89951abd52296adf97deb0723d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/81a161d0b135df89951abd52296adf97deb0723d", + "reference": "81a161d0b135df89951abd52296adf97deb0723d", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-03-21T18:34:15+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.11.1", @@ -8227,16 +8361,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.0.8", + "version": "11.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "48ea58408879a9aad630022186398364051482fc" + "reference": "591bbfe416400385527d5086b346b92c06de404b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/48ea58408879a9aad630022186398364051482fc", - "reference": "48ea58408879a9aad630022186398364051482fc", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/591bbfe416400385527d5086b346b92c06de404b", + "reference": "591bbfe416400385527d5086b346b92c06de404b", "shasum": "" }, "require": { @@ -8307,7 +8441,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.0.8" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.0.9" }, "funding": [ { @@ -8323,7 +8457,7 @@ "type": "tidelift" } ], - "time": "2024-03-22T04:21:01+00:00" + "time": "2024-03-28T10:09:42+00:00" }, { "name": "sebastian/cli-parser", diff --git a/docs/pages/docs/features/ai/server/llama-cpp/index.md b/docs/pages/docs/features/ai/server/llama-cpp/index.md index 88047a59637dd22adad9538ef1fe8cc10d2f02ea..30f63337e76594c3d819d242cebf1197bb7d4ad4 100644 --- a/docs/pages/docs/features/ai/server/llama-cpp/index.md +++ b/docs/pages/docs/features/ai/server/llama-cpp/index.md @@ -46,14 +46,14 @@ you are serving. In the following example we will use namespace App; -use Distantmagic\Resonance\LlamaCppClient; +use Distantmagic\Resonance\LlamaCppClientInterface; use Distantmagic\Resonance\LlamaCppCompletionRequest; use Distantmagic\Resonance\LlamaCppPromptTemplate\MistralInstructChat; #[Singleton] class LlamaCppGenerate { - public function __construct(protected LlamaCppClient $llamaCppClient) + public function __construct(protected LlamaCppClientInterface $llamaCppClient) { } @@ -104,13 +104,13 @@ foreach ($completion as $token) { namespace App; -use Distantmagic\Resonance\LlamaCppClient; +use Distantmagic\Resonance\LlamaCppClientInterface; use Distantmagic\Resonance\LlamaCppEmbeddingRequest; #[Singleton] class LlamaCppGenerate { - public function __construct(protected LlamaCppClient $llamaCppClient) + public function __construct(protected LlamaCppClientInterface $llamaCppClient) { } diff --git a/docs/pages/index.md b/docs/pages/index.md index 663aca8feb4911919921ff8eee97c9cbc2c816e0..b165868b4cd8c47a36a25b035233f72fee631d37 100644 --- a/docs/pages/index.md +++ b/docs/pages/index.md @@ -109,7 +109,7 @@ readonly class CatAdopt implements PromptSubjectResponderInterface data-hljs-language-value="php" >class LlamaCppGenerate { - public function __construct(protected LlamaCppClient $llamaCppClient) + public function __construct(protected LlamaCppClientInterface $llamaCppClient) { } diff --git a/docs/pages/tutorials/how-to-create-llm-websocket-chat-with-llama-cpp/index.md b/docs/pages/tutorials/how-to-create-llm-websocket-chat-with-llama-cpp/index.md index a02b430c8e019498b0edb461f0e79a2ce9e93d1a..81406a4d8607ad23264e8fc432baafd235b05089 100644 --- a/docs/pages/tutorials/how-to-create-llm-websocket-chat-with-llama-cpp/index.md +++ b/docs/pages/tutorials/how-to-create-llm-websocket-chat-with-llama-cpp/index.md @@ -212,7 +212,7 @@ use Distantmagic\Resonance\Feature; use Distantmagic\Resonance\JsonRPCNotification; use Distantmagic\Resonance\JsonRPCRequest; use Distantmagic\Resonance\JsonRPCResponse; -use Distantmagic\Resonance\LlamaCppClient; +use Distantmagic\Resonance\LlamaCppClientInterface; use Distantmagic\Resonance\LlamaCppCompletionRequest; use Distantmagic\Resonance\SingletonCollection; use Distantmagic\Resonance\WebSocketAuthResolution; @@ -225,7 +225,7 @@ use Distantmagic\Resonance\WebSocketJsonRPCResponder; final readonly class LlmChatPromptResponder extends WebSocketJsonRPCResponder { public function __construct( - private LlamaCppClient $llamaCppClient, + private LlamaCppClientInterface $llamaCppClient, ) {} public function getConstraint(): Constraint diff --git a/docs/pages/tutorials/how-to-serve-llm-completions/index.md b/docs/pages/tutorials/how-to-serve-llm-completions/index.md index 4305e4a84ca91e4750a787961b2cab8638d4ac98..09a12072766c094d7109a912e967eceb6c796ef6 100644 --- a/docs/pages/tutorials/how-to-serve-llm-completions/index.md +++ b/docs/pages/tutorials/how-to-serve-llm-completions/index.md @@ -111,13 +111,13 @@ inject `LlamaCppClient`: namespace App; -use Distantmagic\Resonance\LlamaCppClient; +use Distantmagic\Resonance\LlamaCppClientInterface; use Distantmagic\Resonance\LlamaCppCompletionRequest; #[Singleton] class LlamaCppGenerate { - public function __construct(protected LlamaCppClient $llamaCppClient) + public function __construct(protected LlamaCppClientInterface $llamaCppClient) { } diff --git a/phpunit.xml b/phpunit.xml index f960f454e08bb40d9b235874c48330e16c67ef57..3658f46884ff0698c5d749df2ec34df1f1d9c6d3 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -2,7 +2,7 @@ <phpunit beStrictAboutCoverageMetadata="true" beStrictAboutChangesToGlobalState="true" - beStrictAboutTestsThatDoNotTestAnything="false" + beStrictAboutTestsThatDoNotTestAnything="true" bootstrap="phpunit_bootstrap.php" colors="true" displayDetailsOnTestsThatTriggerWarnings="true" diff --git a/src/Attribute/RespondsToHttp.php b/src/Attribute/RespondsToHttp.php index 49890d77e3f4626c9a7f19c2360458b002914e1b..befd9cbe94ceee54b395c5643e305818b18c3660 100644 --- a/src/Attribute/RespondsToHttp.php +++ b/src/Attribute/RespondsToHttp.php @@ -9,7 +9,7 @@ use Distantmagic\Resonance\Attribute as BaseAttribute; use Distantmagic\Resonance\HttpRouteSymbolInterface; use Distantmagic\Resonance\RequestMethod; -#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] +#[Attribute(Attribute::TARGET_CLASS)] final readonly class RespondsToHttp extends BaseAttribute { /** diff --git a/src/Command/LlamaCppGenerate.php b/src/Command/LlamaCppGenerate.php index 2fab6a39bbb45ec2bfacfbf71df945891ced0237..fe2468eebc56e923ba8bb2f025c4e61b476b2c68 100644 --- a/src/Command/LlamaCppGenerate.php +++ b/src/Command/LlamaCppGenerate.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace Distantmagic\Resonance\Command; use Distantmagic\Resonance\CoroutineCommand; -use Distantmagic\Resonance\LlamaCppClient; +use Distantmagic\Resonance\LlamaCppClientInterface; use Distantmagic\Resonance\SwooleConfiguration; use RuntimeException; use Symfony\Component\Console\Input\InputArgument; @@ -20,7 +20,7 @@ abstract class LlamaCppGenerate extends CoroutineCommand abstract protected function executeLlamaCppCommand(InputInterface $input, OutputInterface $output, string $prompt): int; public function __construct( - protected LlamaCppClient $llamaCppClient, + protected LlamaCppClientInterface $llamaCppClient, SwooleConfiguration $swooleConfiguration, ) { parent::__construct($swooleConfiguration); diff --git a/src/Command/LlamaCppGenerate/Embedding.php b/src/Command/LlamaCppGenerate/Embedding.php index 7d5d376a939bfa13542dbd9f24a7d746b75a8ee7..3a9e0545478912adc8af50cbd50e2414911b8b5c 100644 --- a/src/Command/LlamaCppGenerate/Embedding.php +++ b/src/Command/LlamaCppGenerate/Embedding.php @@ -8,7 +8,7 @@ use Distantmagic\Resonance\Attribute\ConsoleCommand; use Distantmagic\Resonance\Command; use Distantmagic\Resonance\Command\LlamaCppGenerate; use Distantmagic\Resonance\JsonSerializer; -use Distantmagic\Resonance\LlamaCppClient; +use Distantmagic\Resonance\LlamaCppClientInterface; use Distantmagic\Resonance\LlamaCppEmbeddingRequest; use Distantmagic\Resonance\SwooleConfiguration; use Symfony\Component\Console\Input\InputInterface; @@ -22,7 +22,7 @@ final class Embedding extends LlamaCppGenerate { public function __construct( private readonly JsonSerializer $jsonSerializer, - LlamaCppClient $llamaCppClient, + LlamaCppClientInterface $llamaCppClient, SwooleConfiguration $swooleConfiguration, ) { parent::__construct($llamaCppClient, $swooleConfiguration); diff --git a/src/Command/LlamaCppHealth.php b/src/Command/LlamaCppHealth.php index a5b64759345dbd66a2c677a9b9a8db3d8f1efbfe..388391c78b372137ffbf4884d7466eae5af7328b 100644 --- a/src/Command/LlamaCppHealth.php +++ b/src/Command/LlamaCppHealth.php @@ -7,7 +7,7 @@ namespace Distantmagic\Resonance\Command; use Distantmagic\Resonance\Attribute\ConsoleCommand; use Distantmagic\Resonance\Command; use Distantmagic\Resonance\CoroutineCommand; -use Distantmagic\Resonance\LlamaCppClient; +use Distantmagic\Resonance\LlamaCppClientInterface; use Distantmagic\Resonance\SwooleConfiguration; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -19,7 +19,7 @@ use Symfony\Component\Console\Output\OutputInterface; final class LlamaCppHealth extends CoroutineCommand { public function __construct( - private readonly LlamaCppClient $llamaCppClient, + private readonly LlamaCppClientInterface $llamaCppClient, SwooleConfiguration $swooleConfiguration, ) { parent::__construct($swooleConfiguration); diff --git a/src/Command/LlamaCppInfill.php b/src/Command/LlamaCppInfill.php index 16f8908e4c793f9d355771b8a458abd1894a752b..ecb5222f97c100aeebdf78cc626d94eb3def4244 100644 --- a/src/Command/LlamaCppInfill.php +++ b/src/Command/LlamaCppInfill.php @@ -8,7 +8,7 @@ use Distantmagic\Resonance\Attribute\ConsoleCommand; use Distantmagic\Resonance\Command; use Distantmagic\Resonance\CoroutineCommand; use Distantmagic\Resonance\JsonSerializer; -use Distantmagic\Resonance\LlamaCppClient; +use Distantmagic\Resonance\LlamaCppClientInterface; use Distantmagic\Resonance\LlamaCppInfillRequest; use Distantmagic\Resonance\SwooleConfiguration; use Symfony\Component\Console\Input\InputInterface; @@ -22,7 +22,7 @@ final class LlamaCppInfill extends CoroutineCommand { public function __construct( private readonly JsonSerializer $jsonSerializer, - private readonly LlamaCppClient $llamaCppClient, + private readonly LlamaCppClientInterface $llamaCppClient, SwooleConfiguration $swooleConfiguration, ) { parent::__construct($swooleConfiguration); diff --git a/src/Command/StaticPagesMakeEmbeddings.php b/src/Command/StaticPagesMakeEmbeddings.php index b948cfc605e9bcd2cc6e653cc07523a2b6e80d50..9b6f8ad18c3746fcdd38d0c50564baf5f78c7ac0 100644 --- a/src/Command/StaticPagesMakeEmbeddings.php +++ b/src/Command/StaticPagesMakeEmbeddings.php @@ -9,7 +9,7 @@ use Distantmagic\Resonance\Attribute\WantsFeature; use Distantmagic\Resonance\Command; use Distantmagic\Resonance\Feature; use Distantmagic\Resonance\JsonSerializer; -use Distantmagic\Resonance\LlamaCppClient; +use Distantmagic\Resonance\LlamaCppClientInterface; use Distantmagic\Resonance\LlamaCppEmbeddingRequest; use Distantmagic\Resonance\SQLiteVSSConnectionBuilder; use Distantmagic\Resonance\StaticPageChunkIterator; @@ -29,7 +29,7 @@ final class StaticPagesMakeEmbeddings extends Command public function __construct( private readonly JsonSerializer $jsonSerializer, - private readonly LlamaCppClient $llamaCppClient, + private readonly LlamaCppClientInterface $llamaCppClient, private readonly SQLiteVSSConnectionBuilder $sqliteVSSConnectionBuilder, private readonly StaticPageChunkIterator $staticPageChunkIterator, ) { diff --git a/src/DialogueInput.php b/src/DialogueInput.php new file mode 100644 index 0000000000000000000000000000000000000000..f22e1acb9d6ac636ecb050a7c858201d6b149aaa --- /dev/null +++ b/src/DialogueInput.php @@ -0,0 +1,7 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +abstract readonly class DialogueInput implements DialogueInputInterface {} diff --git a/src/DialogueInput/UserInput.php b/src/DialogueInput/UserInput.php new file mode 100644 index 0000000000000000000000000000000000000000..10bcb293ac09ae9b0fb694fe5622a1be6cf517aa --- /dev/null +++ b/src/DialogueInput/UserInput.php @@ -0,0 +1,19 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance\DialogueInput; + +use Distantmagic\Resonance\DialogueInputInterface; + +readonly class UserInput implements DialogueInputInterface +{ + public function __construct( + private string $content, + ) {} + + public function getContent(): string + { + return $this->content; + } +} diff --git a/src/DialogueInputInterface.php b/src/DialogueInputInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..d68037a559704c22aa3f8a7f9c3e8a2d6bbfad52 --- /dev/null +++ b/src/DialogueInputInterface.php @@ -0,0 +1,10 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +interface DialogueInputInterface +{ + public function getContent(): string; +} diff --git a/src/DialogueMessageChunk.php b/src/DialogueMessageChunk.php new file mode 100644 index 0000000000000000000000000000000000000000..7df4c9220d1dc48cebfe2518f44482f418774f6c --- /dev/null +++ b/src/DialogueMessageChunk.php @@ -0,0 +1,21 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use Stringable; + +readonly class DialogueMessageChunk implements Stringable +{ + public function __construct( + public string $content, + public bool $isFailed, + public bool $isLastToken, + ) {} + + public function __toString(): string + { + return $this->content; + } +} diff --git a/src/DialogueMessageProducer.php b/src/DialogueMessageProducer.php new file mode 100644 index 0000000000000000000000000000000000000000..c4cfacec725dcc0f33f05484a7dcd978e2ce9a10 --- /dev/null +++ b/src/DialogueMessageProducer.php @@ -0,0 +1,7 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +abstract readonly class DialogueMessageProducer implements DialogueMessageProducerInterface {} diff --git a/src/DialogueMessageProducer/ConstMessageProducer.php b/src/DialogueMessageProducer/ConstMessageProducer.php new file mode 100644 index 0000000000000000000000000000000000000000..4d3a24c803c8596f4182d3a3bd885f3e4ea8fa5e --- /dev/null +++ b/src/DialogueMessageProducer/ConstMessageProducer.php @@ -0,0 +1,28 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance\DialogueMessageProducer; + +use Distantmagic\Resonance\DialogueMessageChunk; +use Distantmagic\Resonance\DialogueMessageProducer; +use Generator; + +readonly class ConstMessageProducer extends DialogueMessageProducer +{ + public function __construct( + public string $content, + ) {} + + /** + * @return Generator<DialogueMessageChunk> + */ + public function getIterator(): Generator + { + yield new DialogueMessageChunk( + content: $this->content, + isFailed: false, + isLastToken: true, + ); + } +} diff --git a/src/DialogueMessageProducer/ConstMessageProducerTest.php b/src/DialogueMessageProducer/ConstMessageProducerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..bbae0441a3f8705afd279fdac7c9965f95fa7b84 --- /dev/null +++ b/src/DialogueMessageProducer/ConstMessageProducerTest.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance\DialogueMessageProducer; + +use Distantmagic\Resonance\DialogueMessageChunk; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; + +/** + * @internal + */ +#[CoversClass(ConstMessageProducer::class)] +final class ConstMessageProducerTest extends TestCase +{ + public function test_message_is_produced(): void + { + $inputMessage = 'What is your current role?'; + $messageProducer = new ConstMessageProducer($inputMessage); + + $message = ''; + + foreach ($messageProducer as $messageChunk) { + self::assertInstanceOf(DialogueMessageChunk::class, $messageChunk); + self::assertFalse($messageChunk->isFailed); + self::assertTrue($messageChunk->isLastToken); + + $message .= $messageChunk->content; + } + + self::assertSame($inputMessage, $message); + } +} diff --git a/src/DialogueMessageProducer/EmptyMessageProducer.php b/src/DialogueMessageProducer/EmptyMessageProducer.php new file mode 100644 index 0000000000000000000000000000000000000000..b3f1fba312cda3496f58b79ca7f11c640feeeeb2 --- /dev/null +++ b/src/DialogueMessageProducer/EmptyMessageProducer.php @@ -0,0 +1,23 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance\DialogueMessageProducer; + +use Distantmagic\Resonance\Attribute\Singleton; +use Distantmagic\Resonance\DialogueMessageChunk; +use Distantmagic\Resonance\DialogueMessageProducer; +use Generator; + +#[Singleton] +readonly class EmptyMessageProducer extends DialogueMessageProducer +{ + public function getIterator(): Generator + { + yield new DialogueMessageChunk( + content: '', + isFailed: false, + isLastToken: true, + ); + } +} diff --git a/src/DialogueMessageProducerInterface.php b/src/DialogueMessageProducerInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..7cdfec891ea1e2d9135ddd97b33ce979d3060c2e --- /dev/null +++ b/src/DialogueMessageProducerInterface.php @@ -0,0 +1,12 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use IteratorAggregate; + +/** + * @template-extends IteratorAggregate<DialogueMessageChunk> + */ +interface DialogueMessageProducerInterface extends IteratorAggregate {} diff --git a/src/DialogueNode.php b/src/DialogueNode.php new file mode 100644 index 0000000000000000000000000000000000000000..5943acf3a137d3a3cf680a1ddb8d70e54e9ffcdf --- /dev/null +++ b/src/DialogueNode.php @@ -0,0 +1,37 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use Ds\Set; + +readonly class DialogueNode implements DialogueNodeInterface +{ + /** + * @var Set<DialogueResponseInterface> + */ + private Set $responses; + + public function __construct( + private DialogueMessageProducerInterface $message, + private DialogueResponseDiscriminatorInterface $responseDiscriminator, + ) { + $this->responses = new Set(); + } + + public function addResponse(DialogueResponseInterface $response): void + { + $this->responses->add($response); + } + + public function getMessageProducer(): DialogueMessageProducerInterface + { + return $this->message; + } + + public function respondTo(DialogueInputInterface $prompt): ?DialogueNodeInterface + { + return $this->responseDiscriminator->discriminate($this->responses, $prompt); + } +} diff --git a/src/DialogueNodeInterface.php b/src/DialogueNodeInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..875b1c9a23c6864555003f7fc60ad349687e36f4 --- /dev/null +++ b/src/DialogueNodeInterface.php @@ -0,0 +1,14 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +interface DialogueNodeInterface +{ + public function addResponse(DialogueResponseInterface $response): void; + + public function getMessageProducer(): DialogueMessageProducerInterface; + + public function respondTo(DialogueInputInterface $prompt): ?self; +} diff --git a/src/DialogueNodeTest.php b/src/DialogueNodeTest.php new file mode 100644 index 0000000000000000000000000000000000000000..64b86b70cd5b0c92c2eabc53cf9274dd3d77c01c --- /dev/null +++ b/src/DialogueNodeTest.php @@ -0,0 +1,62 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use Distantmagic\Resonance\DialogueInput\UserInput; +use Distantmagic\Resonance\DialogueMessageProducer\ConstMessageProducer; +use Distantmagic\Resonance\DialogueResponseCondition\ExactInputCondition; +use Distantmagic\Resonance\DialogueResponseCondition\LlamaCppInputCondition; +use Mockery; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; + +/** + * @internal + */ +#[CoversClass(DialogueNode::class)] +final class DialogueNodeTest extends TestCase +{ + public function test_dialogue_produces_no_response(): void + { + $responseDiscriminator = new DialogueResponseDiscriminator(); + + $rootNode = new DialogueNode( + message: new ConstMessageProducer('What is your current role?'), + responseDiscriminator: $responseDiscriminator, + ); + + $marketingNode = new DialogueNode( + message: new ConstMessageProducer('Hello, marketer!'), + responseDiscriminator: $responseDiscriminator, + ); + + $rootNode->addResponse(new DialogueResponse( + when: new LlamaCppInputCondition( + Mockery::mock(LlamaCppClientInterface::class), + 'marketing' + ), + followUp: $marketingNode, + )); + + $rootNode->addResponse(new DialogueResponse( + when: new ExactInputCondition('marketing'), + followUp: $marketingNode, + )); + + $invalidNode = new DialogueNode( + message: new ConstMessageProducer('nope :('), + responseDiscriminator: $responseDiscriminator, + ); + + $rootNode->addResponse(new DialogueResponse( + when: new ExactInputCondition('not_a_marketing'), + followUp: $invalidNode, + )); + + $response = $rootNode->respondTo(new UserInput('marketing')); + + self::assertSame($response, $marketingNode); + } +} diff --git a/src/DialogueResponse.php b/src/DialogueResponse.php new file mode 100644 index 0000000000000000000000000000000000000000..8dfb37d4ee108db17deb2a229770eb9a6d4a38d3 --- /dev/null +++ b/src/DialogueResponse.php @@ -0,0 +1,23 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +readonly class DialogueResponse implements DialogueResponseInterface +{ + public function __construct( + private DialogueNodeInterface $followUp, + private DialogueResponseConditionInterface $when, + ) {} + + public function getCondition(): DialogueResponseConditionInterface + { + return $this->when; + } + + public function getFollowUp(): DialogueNodeInterface + { + return $this->followUp; + } +} diff --git a/src/DialogueResponseCondition.php b/src/DialogueResponseCondition.php new file mode 100644 index 0000000000000000000000000000000000000000..9196226c7d0edcd8a9aa9807ad6fee97a6a9ae2b --- /dev/null +++ b/src/DialogueResponseCondition.php @@ -0,0 +1,7 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +abstract readonly class DialogueResponseCondition implements DialogueResponseConditionInterface {} diff --git a/src/DialogueResponseCondition/ExactInputCondition.php b/src/DialogueResponseCondition/ExactInputCondition.php new file mode 100644 index 0000000000000000000000000000000000000000..20449344fc631b8271de78abd250cd46bb46e6e5 --- /dev/null +++ b/src/DialogueResponseCondition/ExactInputCondition.php @@ -0,0 +1,25 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance\DialogueResponseCondition; + +use Distantmagic\Resonance\DialogueInputInterface; +use Distantmagic\Resonance\DialogueResponseCondition; + +readonly class ExactInputCondition extends DialogueResponseCondition +{ + public function __construct( + public string $content, + ) {} + + public function getCost(): int + { + return 2; + } + + public function isMetBy(DialogueInputInterface $dialogueInput): bool + { + return $dialogueInput->getContent() === $this->content; + } +} diff --git a/src/DialogueResponseCondition/LlamaCppInputCondition.php b/src/DialogueResponseCondition/LlamaCppInputCondition.php new file mode 100644 index 0000000000000000000000000000000000000000..97d99fe5192e4e9a83d9501a0b9ff1fbc88334bc --- /dev/null +++ b/src/DialogueResponseCondition/LlamaCppInputCondition.php @@ -0,0 +1,24 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance\DialogueResponseCondition; + +use Distantmagic\Resonance\DialogueInputInterface; +use Distantmagic\Resonance\DialogueResponseCondition; +use Distantmagic\Resonance\LlamaCppClientInterface; + +readonly class LlamaCppInputCondition extends DialogueResponseCondition +{ + public function __construct( + public LlamaCppClientInterface $llamaCppClient, + public string $content, + ) {} + + public function getCost(): int + { + return 50; + } + + public function isMetBy(DialogueInputInterface $dialogueInput): bool {} +} diff --git a/src/DialogueResponseConditionInterface.php b/src/DialogueResponseConditionInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..80e50ff9632854908d05f9677910eecc8439dd23 --- /dev/null +++ b/src/DialogueResponseConditionInterface.php @@ -0,0 +1,12 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +interface DialogueResponseConditionInterface +{ + public function getCost(): int; + + public function isMetBy(DialogueInputInterface $dialogueInput): bool; +} diff --git a/src/DialogueResponseDiscriminator.php b/src/DialogueResponseDiscriminator.php new file mode 100644 index 0000000000000000000000000000000000000000..628af587b2a8fe99180ef7a567ba3609e0343d81 --- /dev/null +++ b/src/DialogueResponseDiscriminator.php @@ -0,0 +1,27 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use Distantmagic\Resonance\Attribute\Singleton; + +#[Singleton] +readonly class DialogueResponseDiscriminator implements DialogueResponseDiscriminatorInterface +{ + /** + * @param iterable<DialogueResponseInterface> $responses + */ + public function discriminate( + iterable $responses, + DialogueInputInterface $dialogueInput, + ): ?DialogueNodeInterface { + foreach (new DialogueResponseSortedIterator($responses) as $response) { + if ($response->getCondition()->isMetBy($dialogueInput)) { + return $response->getFollowUp(); + } + } + + return null; + } +} diff --git a/src/DialogueResponseDiscriminatorInterface.php b/src/DialogueResponseDiscriminatorInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..60002459ba11e08cb6041fcd950ff855059e3567 --- /dev/null +++ b/src/DialogueResponseDiscriminatorInterface.php @@ -0,0 +1,16 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +interface DialogueResponseDiscriminatorInterface +{ + /** + * @param iterable<DialogueResponseInterface> $responses + */ + public function discriminate( + iterable $responses, + DialogueInputInterface $dialogueInput, + ): ?DialogueNodeInterface; +} diff --git a/src/DialogueResponseInterface.php b/src/DialogueResponseInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..df2c67845f32a84753006ae2fb7f812b96f3d21f --- /dev/null +++ b/src/DialogueResponseInterface.php @@ -0,0 +1,12 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +interface DialogueResponseInterface +{ + public function getCondition(): DialogueResponseConditionInterface; + + public function getFollowUp(): DialogueNodeInterface; +} diff --git a/src/DialogueResponseResolution.php b/src/DialogueResponseResolution.php new file mode 100644 index 0000000000000000000000000000000000000000..3dcfa0d8b2c93289805253453b5698170efce829 --- /dev/null +++ b/src/DialogueResponseResolution.php @@ -0,0 +1,17 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +readonly class DialogueResponseResolution implements DialogueResponseResolutionInterface +{ + public function __construct( + private DialogueResponseResolutionStatus $status, + ) {} + + public function getStatus(): DialogueResponseResolutionStatus + { + return $this->status; + } +} diff --git a/src/DialogueResponseResolutionInterface.php b/src/DialogueResponseResolutionInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..6ee9d1b4a82c6881f6126ff8e166fc7462430e53 --- /dev/null +++ b/src/DialogueResponseResolutionInterface.php @@ -0,0 +1,10 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +interface DialogueResponseResolutionInterface +{ + public function getStatus(): DialogueResponseResolutionStatus; +} diff --git a/src/DialogueResponseResolutionStatus.php b/src/DialogueResponseResolutionStatus.php new file mode 100644 index 0000000000000000000000000000000000000000..136588fabb47a5d45e3dbbdb7e2c164bc5ca27ae --- /dev/null +++ b/src/DialogueResponseResolutionStatus.php @@ -0,0 +1,7 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +enum DialogueResponseResolutionStatus {} diff --git a/src/DialogueResponseSortedIterator.php b/src/DialogueResponseSortedIterator.php new file mode 100644 index 0000000000000000000000000000000000000000..de0aa5792e9efdc1b83173d4e25bb565ad130cc8 --- /dev/null +++ b/src/DialogueResponseSortedIterator.php @@ -0,0 +1,54 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use Ds\PriorityQueue; +use Ds\Stack; +use Generator; +use IteratorAggregate; + +/** + * @template-implements IteratorAggregate<DialogueResponseInterface> + */ +readonly class DialogueResponseSortedIterator implements IteratorAggregate +{ + /** + * @param iterable<DialogueResponseInterface> $responses + */ + public function __construct( + private iterable $responses, + ) {} + + /** + * @return Generator<DialogueResponseInterface> + */ + public function getIterator(): Generator + { + /** + * @var PriorityQueue<DialogueResponseInterface> $responsesPriorityQueue + */ + $responsesPriorityQueue = new PriorityQueue(); + + foreach ($this->responses as $response) { + $responsesPriorityQueue->push( + $response, + $response->getCondition()->getCost(), + ); + } + + /** + * @var Stack<DialogueResponseInterface> $sortedResponses + */ + $sortedResponses = new Stack(); + + foreach ($responsesPriorityQueue as $response) { + $sortedResponses->push($response); + } + + foreach ($sortedResponses as $response) { + yield $response; + } + } +} diff --git a/src/DialogueResponseSortedIteratorTest.php b/src/DialogueResponseSortedIteratorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..82fa3b2617c1e1efc6022b42cc27d161f43318e6 --- /dev/null +++ b/src/DialogueResponseSortedIteratorTest.php @@ -0,0 +1,54 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use Distantmagic\Resonance\DialogueMessageProducer\ConstMessageProducer; +use Distantmagic\Resonance\DialogueResponseCondition\ExactInputCondition; +use Distantmagic\Resonance\DialogueResponseCondition\LlamaCppInputCondition; +use Mockery; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; + +/** + * @internal + */ +#[CoversClass(DialogueResponseSortedIterator::class)] +final class DialogueResponseSortedIteratorTest extends TestCase +{ + public function test_dialogue_responses_are_sorted_by_cost(): void + { + $responseDiscriminator = new DialogueResponseDiscriminator(); + + $marketingNode = new DialogueNode( + message: new ConstMessageProducer('Hello, marketer!'), + responseDiscriminator: $responseDiscriminator, + ); + + $response1 = new DialogueResponse( + when: new LlamaCppInputCondition( + Mockery::mock(LlamaCppClientInterface::class), + 'test' + ), + followUp: $marketingNode, + ); + + $response2 = new DialogueResponse( + when: new ExactInputCondition('marketing'), + followUp: $marketingNode, + ); + + $responses = [ + $response1, + $response2, + ]; + + $sortedResponses = iterator_to_array(new DialogueResponseSortedIterator($responses)); + + self::assertEquals([ + $response2, + $response1, + ], $sortedResponses); + } +} diff --git a/src/EsbuildMetaBuilder.php b/src/EsbuildMetaBuilder.php index 3c8720624dcb1cbd503d022b0abc68d1320f8ad5..91565d5f92de924e9610f61748695d3092e2fa4a 100644 --- a/src/EsbuildMetaBuilder.php +++ b/src/EsbuildMetaBuilder.php @@ -151,13 +151,13 @@ readonly class EsbuildMetaBuilder throw new RuntimeException('Esbuild meta manifest is not readable: '.$esbuildMetafile); } - $contents = Coroutine::readFile($esbuildMetafile); + $content = Coroutine::readFile($esbuildMetafile); - if (!is_string($contents)) { + if (!is_string($content)) { throw new RuntimeException('Unable to read esbuild manifest: '.$esbuildMetafile); } - return $contents; + return $content; } private function getEsbuildMetaDecoded(string $esbuildMetafile): object diff --git a/src/HttpInterceptor.php b/src/HttpInterceptor.php index d0aad021b042f22696f938ce4ecfe3945c356fd1..d0f4e5a62629dff9470f60bb4b040ce4eada9f51 100644 --- a/src/HttpInterceptor.php +++ b/src/HttpInterceptor.php @@ -14,8 +14,8 @@ use Stringable; */ abstract readonly class HttpInterceptor implements HttpInterceptorInterface { - public function createStream(string|Stringable $contents): StreamInterface + public function createStream(string|Stringable $content): StreamInterface { - return new PsrStringStream($contents); + return new PsrStringStream($content); } } diff --git a/src/HttpResponder.php b/src/HttpResponder.php index 85b40c6d60c1addc2a536059161472c2c83507f3..a00045148596e646ac10224230bb9347bcaa9f57 100644 --- a/src/HttpResponder.php +++ b/src/HttpResponder.php @@ -9,8 +9,8 @@ use Stringable; abstract readonly class HttpResponder implements HttpResponderInterface { - public function createStream(string|Stringable $contents): StreamInterface + public function createStream(string|Stringable $content): StreamInterface { - return new PsrStringStream($contents); + return new PsrStringStream($content); } } diff --git a/src/LlamaCppClient.php b/src/LlamaCppClient.php index b991ee4ee092b22cc7f71bdb0bf7aa53cfa10ed2..831878abbe49f8fbb6f5f1eaffc3fa8f6402fef9 100644 --- a/src/LlamaCppClient.php +++ b/src/LlamaCppClient.php @@ -15,8 +15,8 @@ use Swoole\Coroutine; use Swoole\Coroutine\Channel; #[RequiresPhpExtension('curl')] -#[Singleton] -readonly class LlamaCppClient +#[Singleton(provides: LlamaCppClientInterface::class)] +readonly class LlamaCppClient implements LlamaCppClientInterface { public function __construct( private JsonSerializer $jsonSerializer, diff --git a/src/LlamaCppClientInterface.php b/src/LlamaCppClientInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..c2ce4b469331a3f519cd9fbd05c7e00f88423c68 --- /dev/null +++ b/src/LlamaCppClientInterface.php @@ -0,0 +1,21 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use Generator; + +interface LlamaCppClientInterface +{ + public function generateCompletion(LlamaCppCompletionRequest $request): LlamaCppCompletionIterator; + + public function generateEmbedding(LlamaCppEmbeddingRequest $request): LlamaCppEmbedding; + + /** + * @return Generator<LlamaCppInfill> + */ + public function generateInfill(LlamaCppInfillRequest $request): Generator; + + public function getHealth(): LlamaCppHealthStatus; +} diff --git a/src/PsrStringStream.php b/src/PsrStringStream.php index 8c9dc8a344d2e09d7cadf224e18d4497f12f202b..94d5b0ddf51962fdf897cbad8509fe72776e6bd3 100644 --- a/src/PsrStringStream.php +++ b/src/PsrStringStream.php @@ -9,16 +9,16 @@ use Stringable; readonly class PsrStringStream implements StreamInterface { - private string $contents; + private string $content; - public function __construct(string|Stringable $contents) + public function __construct(string|Stringable $content) { - $this->contents = (string) $contents; + $this->content = (string) $content; } public function __toString(): string { - return $this->contents; + return $this->content; } public function close(): void {} @@ -32,7 +32,7 @@ readonly class PsrStringStream implements StreamInterface public function getContents(): string { - return $this->contents; + return $this->content; } public function getMetadata($key = null) @@ -42,7 +42,7 @@ readonly class PsrStringStream implements StreamInterface public function getSize(): ?int { - return strlen($this->contents); + return strlen($this->content); } public function isReadable(): bool diff --git a/src/StaticPageInternalLinkNodeRenderer.php b/src/StaticPageInternalLinkNodeRenderer.php index b0ba59a1e99d1f65e7491654bc437043f07bd746..9df9cf1c7d739b445cd7581cd8ef879991857eec 100644 --- a/src/StaticPageInternalLinkNodeRenderer.php +++ b/src/StaticPageInternalLinkNodeRenderer.php @@ -103,9 +103,9 @@ readonly class StaticPageInternalLinkNodeRenderer implements NodeRendererInterfa private function renderStaticPageBlockLink(StaticPage $staticPage): Stringable { /** - * @list<HtmlElement> $contents + * @list<HtmlElement> $content */ - $contents = [ + $content = [ new HtmlElement( 'div', [ @@ -135,7 +135,7 @@ readonly class StaticPageInternalLinkNodeRenderer implements NodeRendererInterfa ); } - $contents[] = new HtmlElement( + $content[] = new HtmlElement( 'ol', [ 'class' => 'document-links-group__link__tags', @@ -150,7 +150,7 @@ readonly class StaticPageInternalLinkNodeRenderer implements NodeRendererInterfa 'class' => 'document-links-group__link', 'href' => $staticPage->getHref(), ], - $contents, + $content, ); } diff --git a/src/WebSocketJsonRPCResponder/LlamaCppSubjectActionPromptResponder.php b/src/WebSocketJsonRPCResponder/LlamaCppSubjectActionPromptResponder.php index 8c3603dec450c351275487fd87813a9b4508e457..bf45ed90a19bddccaa6b80eb79ba57b90d56ca11 100644 --- a/src/WebSocketJsonRPCResponder/LlamaCppSubjectActionPromptResponder.php +++ b/src/WebSocketJsonRPCResponder/LlamaCppSubjectActionPromptResponder.php @@ -6,7 +6,7 @@ namespace Distantmagic\Resonance\WebSocketJsonRPCResponder; use Distantmagic\Resonance\BackusNaurFormGrammar\SubjectActionGrammar; use Distantmagic\Resonance\JsonRPCRequest; -use Distantmagic\Resonance\LlamaCppClient; +use Distantmagic\Resonance\LlamaCppClientInterface; use Distantmagic\Resonance\LlamaCppCompletionIterator; use Distantmagic\Resonance\LlamaCppCompletionRequest; use Distantmagic\Resonance\LlmPrompt\SubjectActionPrompt; @@ -59,7 +59,7 @@ abstract readonly class LlamaCppSubjectActionPromptResponder extends WebSocketJs abstract protected function toPromptTemplate(string $prompt): LlmPromptTemplate; public function __construct( - private LlamaCppClient $llamaCppClient, + private LlamaCppClientInterface $llamaCppClient, private LoggerInterface $logger, private ObservableTaskTable $observableTaskTable, private PromptSubjectResponderAggregate $promptSubjectResponderAggregate,