From 3d2eeebeec6ea754f9a5e44a9b12d6d851541c11 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Sun, 29 Jun 2025 23:54:08 +0200 Subject: [PATCH 01/10] feat: add Fabric patterns support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements integration with Fabric patterns from danielmiessler/fabric, providing a collection of pre-built, tested system prompts for common AI tasks. Key features: - Message::fabric() factory method for easy pattern usage - FabricInputProcessor for dynamic pattern loading via chain options - FabricRepository for pattern management with caching - Support for custom pattern locations - Comprehensive test coverage Usage: 1. Install the patterns: composer require php-llm/fabric-pattern 2. Use via factory: Message::fabric('create_summary') 3. Or via processor: $chain->call($messages, ['fabric_pattern' => 'analyze_code']) The implementation requires the separate php-llm/fabric-pattern package to be installed, keeping the main library lightweight while allowing users to opt-in to Fabric patterns functionality. Closes #31 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- README.md | 76 ++++++++ examples/fabric/summarize.php | 59 ++++++ examples/fabric/with-processor.php | 59 ++++++ .../Exception/PatternNotFoundException.php | 12 ++ src/Platform/Fabric/FabricInputProcessor.php | 53 ++++++ src/Platform/Fabric/FabricPrompt.php | 36 ++++ src/Platform/Fabric/FabricPromptInterface.php | 28 +++ src/Platform/Fabric/FabricRepository.php | 138 ++++++++++++++ src/Platform/Message/Message.php | 18 ++ .../Fabric/FabricInputProcessorTest.php | 148 +++++++++++++++ tests/Platform/Fabric/FabricPromptTest.php | 40 ++++ .../Platform/Fabric/FabricRepositoryTest.php | 171 ++++++++++++++++++ tests/Platform/Message/MessageFabricTest.php | 55 ++++++ 13 files changed, 893 insertions(+) create mode 100644 examples/fabric/summarize.php create mode 100644 examples/fabric/with-processor.php create mode 100644 src/Platform/Fabric/Exception/PatternNotFoundException.php create mode 100644 src/Platform/Fabric/FabricInputProcessor.php create mode 100644 src/Platform/Fabric/FabricPrompt.php create mode 100644 src/Platform/Fabric/FabricPromptInterface.php create mode 100644 src/Platform/Fabric/FabricRepository.php create mode 100644 tests/Platform/Fabric/FabricInputProcessorTest.php create mode 100644 tests/Platform/Fabric/FabricPromptTest.php create mode 100644 tests/Platform/Fabric/FabricRepositoryTest.php create mode 100644 tests/Platform/Message/MessageFabricTest.php diff --git a/README.md b/README.md index c6a141d3..7c57454c 100644 --- a/README.md +++ b/README.md @@ -769,6 +769,82 @@ final class MyProcessor implements OutputProcessorInterface, ChainAwareInterface } ``` +### Fabric Patterns + +LLM Chain supports [Fabric](https://github.com/danielmiessler/fabric), a popular collection of system prompts from the AI community. These patterns provide pre-built, tested prompts for common tasks like summarization, analysis, and content creation. + +> [!NOTE] +> Using Fabric patterns requires the `php-llm/fabric-pattern` package to be installed separately. + +#### Installation + +```bash +composer require php-llm/fabric-pattern +``` + +#### Usage with Message Factory + +The simplest way to use Fabric patterns is through the `Message::fabric()` factory method: + +```php +use PhpLlm\LlmChain\Platform\Message\Message; +use PhpLlm\LlmChain\Platform\Message\MessageBag; + +$messages = new MessageBag( + Message::fabric('create_summary'), + Message::ofUser($articleContent) +); + +$response = $chain->call($messages); +``` + +#### Usage with Input Processor + +For more flexibility, you can use the `FabricInputProcessor` to dynamically load patterns: + +```php +use PhpLlm\LlmChain\Chain\Chain; +use PhpLlm\LlmChain\Platform\Fabric\FabricInputProcessor; + +// Initialize Platform and LLM + +$processor = new FabricInputProcessor(); +$chain = new Chain($platform, $model, [$processor]); + +$messages = new MessageBag( + Message::ofUser('Analyze this article for potential security issues: ...') +); + +// Use any Fabric pattern via options +$response = $chain->call($messages, ['fabric_pattern' => 'analyze_threat_report']); +``` + +#### Custom Pattern Locations + +If you have your own collection of patterns or want to use a local copy: + +```php +use PhpLlm\LlmChain\Platform\Fabric\FabricRepository; + +// Use custom pattern directory +$repository = new FabricRepository('/path/to/custom/patterns'); +$processor = new FabricInputProcessor($repository); + +// Or with the factory method +$message = Message::fabric('my_custom_pattern', '/path/to/custom/patterns'); +``` + +#### Available Patterns + +Some popular Fabric patterns include: +- `create_summary` - Create a comprehensive summary +- `analyze_claims` - Analyze and fact-check claims +- `extract_wisdom` - Extract key insights and wisdom +- `improve_writing` - Improve writing quality and clarity +- `create_quiz` - Generate quiz questions from content + +For a full list of available patterns, visit the [Fabric patterns directory](https://github.com/danielmiessler/fabric/tree/main/patterns). + ## HuggingFace LLM Chain comes out of the box with an integration for [HuggingFace](https://huggingface.co/) which is a platform for diff --git a/examples/fabric/summarize.php b/examples/fabric/summarize.php new file mode 100644 index 00000000..824251a4 --- /dev/null +++ b/examples/fabric/summarize.php @@ -0,0 +1,59 @@ +call($messages); + + echo "Summary using Fabric pattern:".PHP_EOL; + echo "==============================".PHP_EOL; + echo $response->getContent().PHP_EOL; + +} catch (\RuntimeException $e) { + if (str_contains($e->getMessage(), 'php-llm/fabric-pattern')) { + echo "Fabric patterns are not installed.".PHP_EOL; + echo "Please install them with: composer require php-llm/fabric-pattern".PHP_EOL; + exit(1); + } + throw $e; +} \ No newline at end of file diff --git a/examples/fabric/with-processor.php b/examples/fabric/with-processor.php new file mode 100644 index 00000000..946ed43e --- /dev/null +++ b/examples/fabric/with-processor.php @@ -0,0 +1,59 @@ +call($messages, ['fabric_pattern' => 'analyze_code']); + + echo "Code Analysis using Fabric pattern:".PHP_EOL; + echo "===================================".PHP_EOL; + echo $response->getContent().PHP_EOL; + +} catch (\RuntimeException $e) { + if (str_contains($e->getMessage(), 'php-llm/fabric-pattern')) { + echo "Fabric patterns are not installed.".PHP_EOL; + echo "Please install them with: composer require php-llm/fabric-pattern".PHP_EOL; + exit(1); + } + throw $e; +} \ No newline at end of file diff --git a/src/Platform/Fabric/Exception/PatternNotFoundException.php b/src/Platform/Fabric/Exception/PatternNotFoundException.php new file mode 100644 index 00000000..8b4c2142 --- /dev/null +++ b/src/Platform/Fabric/Exception/PatternNotFoundException.php @@ -0,0 +1,12 @@ +repository = $repository; + } + + public function processInput(Input $input): void + { + $options = $input->getOptions(); + + if (!isset($options['fabric_pattern'])) { + return; + } + + $pattern = $options['fabric_pattern']; + if (!\is_string($pattern)) { + throw new \InvalidArgumentException('The "fabric_pattern" option must be a string'); + } + + // Load the pattern and prepend as system message + $repository = $this->repository ?? new FabricRepository(); + $fabricPrompt = $repository->load($pattern); + $systemMessage = new SystemMessage($fabricPrompt->getContent()); + + // Prepend the system message + $input->messages = $input->messages->prepend($systemMessage); + + // Remove the fabric option from the chain options + unset($options['fabric_pattern']); + $input->setOptions($options); + } +} diff --git a/src/Platform/Fabric/FabricPrompt.php b/src/Platform/Fabric/FabricPrompt.php new file mode 100644 index 00000000..a55b8c1b --- /dev/null +++ b/src/Platform/Fabric/FabricPrompt.php @@ -0,0 +1,36 @@ + $metadata + */ + public function __construct( + private string $pattern, + private string $content, + private array $metadata = [], + ) { + } + + public function getPattern(): string + { + return $this->pattern; + } + + public function getContent(): string + { + return $this->content; + } + + public function getMetadata(): array + { + return $this->metadata; + } +} diff --git a/src/Platform/Fabric/FabricPromptInterface.php b/src/Platform/Fabric/FabricPromptInterface.php new file mode 100644 index 00000000..4c74eb3b --- /dev/null +++ b/src/Platform/Fabric/FabricPromptInterface.php @@ -0,0 +1,28 @@ + + */ + public function getMetadata(): array; +} diff --git a/src/Platform/Fabric/FabricRepository.php b/src/Platform/Fabric/FabricRepository.php new file mode 100644 index 00000000..8e3cfe83 --- /dev/null +++ b/src/Platform/Fabric/FabricRepository.php @@ -0,0 +1,138 @@ + + */ + private array $cache = []; + + public function __construct(?string $patternsPath = null) + { + if (null === $patternsPath) { + // Check if fabric-pattern package is installed + $fabricPatternPath = \dirname(__DIR__, 4).'/fabric-pattern/patterns'; + if (is_dir($fabricPatternPath)) { + $this->patternsPath = $fabricPatternPath; + } else { + throw new \RuntimeException( + 'Fabric patterns not found. Please install the "php-llm/fabric-pattern" package: composer require php-llm/fabric-pattern' + ); + } + } else { + $this->patternsPath = $patternsPath; + } + } + + /** + * Load a Fabric pattern by name. + * + * @throws PatternNotFoundException + */ + public function load(string $pattern): FabricPromptInterface + { + if (isset($this->cache[$pattern])) { + return $this->cache[$pattern]; + } + + $patternPath = $this->patternsPath.'/'.$pattern; + $systemFile = $patternPath.'/system.md'; + + if (!is_dir($patternPath) || !is_file($systemFile)) { + throw new PatternNotFoundException(\sprintf('Pattern "%s" not found at path "%s"', $pattern, $patternPath)); + } + + $content = file_get_contents($systemFile); + if (false === $content) { + throw new PatternNotFoundException(\sprintf('Could not read system.md for pattern "%s"', $pattern)); + } + + $metadata = $this->loadMetadata($patternPath); + + return $this->cache[$pattern] = new FabricPrompt($pattern, $content, $metadata); + } + + /** + * List all available patterns. + * + * @return string[] + */ + public function listPatterns(): array + { + if (!is_dir($this->patternsPath)) { + return []; + } + + $patterns = []; + $directories = scandir($this->patternsPath); + + if (false === $directories) { + return []; + } + + foreach ($directories as $dir) { + if ('.' === $dir || '..' === $dir) { + continue; + } + + $systemFile = $this->patternsPath.'/'.$dir.'/system.md'; + if (is_dir($this->patternsPath.'/'.$dir) && is_file($systemFile)) { + $patterns[] = $dir; + } + } + + sort($patterns); + + return $patterns; + } + + /** + * Check if a pattern exists. + */ + public function exists(string $pattern): bool + { + $patternPath = $this->patternsPath.'/'.$pattern; + $systemFile = $patternPath.'/system.md'; + + return is_dir($patternPath) && is_file($systemFile); + } + + /** + * @return array + */ + private function loadMetadata(string $patternPath): array + { + $metadata = []; + + // Check for README.md + $readmeFile = $patternPath.'/README.md'; + if (is_file($readmeFile)) { + $metadata['readme'] = file_get_contents($readmeFile) ?: ''; + } + + // Check for other metadata files + $metadataFile = $patternPath.'/metadata.json'; + if (is_file($metadataFile)) { + $jsonContent = file_get_contents($metadataFile); + if (false !== $jsonContent) { + $decoded = json_decode($jsonContent, true); + if (\is_array($decoded)) { + $metadata = array_merge($metadata, $decoded); + } + } + } + + return $metadata; + } +} diff --git a/src/Platform/Message/Message.php b/src/Platform/Message/Message.php index f15ad17b..d3972fff 100644 --- a/src/Platform/Message/Message.php +++ b/src/Platform/Message/Message.php @@ -4,6 +4,7 @@ namespace PhpLlm\LlmChain\Platform\Message; +use PhpLlm\LlmChain\Platform\Fabric\FabricRepository; use PhpLlm\LlmChain\Platform\Message\Content\ContentInterface; use PhpLlm\LlmChain\Platform\Message\Content\Text; use PhpLlm\LlmChain\Platform\Response\ToolCall; @@ -24,6 +25,23 @@ public static function forSystem(\Stringable|string $content): SystemMessage return new SystemMessage($content instanceof \Stringable ? (string) $content : $content); } + /** + * Create a SystemMessage from a Fabric pattern. + * + * Requires the "php-llm/fabric-pattern" package to be installed. + * + * @param string|null $patternsPath Optional custom patterns path + * + * @throws \RuntimeException if fabric-pattern package is not installed + */ + public static function fabric(string $pattern, ?string $patternsPath = null): SystemMessage + { + $repository = new FabricRepository($patternsPath); + $fabricPrompt = $repository->load($pattern); + + return new SystemMessage($fabricPrompt->getContent()); + } + /** * @param ?ToolCall[] $toolCalls */ diff --git a/tests/Platform/Fabric/FabricInputProcessorTest.php b/tests/Platform/Fabric/FabricInputProcessorTest.php new file mode 100644 index 00000000..32791ffa --- /dev/null +++ b/tests/Platform/Fabric/FabricInputProcessorTest.php @@ -0,0 +1,148 @@ +createMock(FabricRepository::class); + $repository->expects($this->once()) + ->method('load') + ->with('test_pattern') + ->willReturn(new FabricPrompt('test_pattern', '# Fabric Content')); + + $processor = new FabricInputProcessor($repository); + + $messages = new MessageBag(); + $model = new Model('test-model', []); + $input = new Input($model, $messages, ['fabric_pattern' => 'test_pattern']); + + $processor->processInput($input); + + self::assertCount(1, $input->messages); + $messages = $input->messages->getMessages(); + self::assertInstanceOf(SystemMessage::class, $messages[0]); + self::assertSame('# Fabric Content', $messages[0]->content); + self::assertArrayNotHasKey('fabric_pattern', $input->getOptions()); + } + + #[Test] + public function processInputWithoutFabricPattern(): void + { + $repository = $this->createMock(FabricRepository::class); + $repository->expects($this->never())->method('load'); + + $processor = new FabricInputProcessor($repository); + + $messages = new MessageBag(); + $model = new Model('test-model', []); + $input = new Input($model, $messages, ['temperature' => 0.7]); + + $processor->processInput($input); + + self::assertCount(0, $input->messages); + self::assertSame(['temperature' => 0.7], $input->getOptions()); + } + + #[Test] + public function processInputWithDefaultRepositoryThrowsExceptionWhenPackageNotInstalled(): void + { + $processor = new FabricInputProcessor(); + + $messages = new MessageBag(); + $model = new Model('test-model', []); + $input = new Input($model, $messages, ['fabric_pattern' => 'test_pattern']); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Fabric patterns not found. Please install the "php-llm/fabric-pattern" package'); + + $processor->processInput($input); + } + + #[Test] + public function processInputWithCustomPatternsPath(): void + { + $testPath = sys_get_temp_dir().'/fabric-test-'.uniqid(); + mkdir($testPath.'/test_pattern', 0777, true); + file_put_contents($testPath.'/test_pattern/system.md', '# Custom Path Content'); + + $repository = new FabricRepository($testPath); + $processor = new FabricInputProcessor($repository); + + $messages = new MessageBag(); + $model = new Model('test-model', []); + + $input = new Input($model, $messages, [ + 'fabric_pattern' => 'test_pattern', + ]); + + try { + $processor->processInput($input); + + self::assertCount(1, $input->messages); + $messages = $input->messages->getMessages(); + self::assertInstanceOf(SystemMessage::class, $messages[0]); + self::assertSame('# Custom Path Content', $messages[0]->content); + self::assertArrayNotHasKey('fabric_pattern', $input->getOptions()); + } finally { + // Cleanup + unlink($testPath.'/test_pattern/system.md'); + rmdir($testPath.'/test_pattern'); + rmdir($testPath); + } + } + + #[Test] + public function processInputWithInvalidPatternType(): void + { + $processor = new FabricInputProcessor(); + + $messages = new MessageBag(); + $model = new Model('test-model', []); + $input = new Input($model, $messages, ['fabric_pattern' => 123]); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('The "fabric_pattern" option must be a string'); + + $processor->processInput($input); + } + + #[Test] + public function processInputPatternNotFound(): void + { + $repository = $this->createMock(FabricRepository::class); + $repository->expects($this->once()) + ->method('load') + ->with('non_existing') + ->willThrowException(new PatternNotFoundException('Pattern not found')); + + $processor = new FabricInputProcessor($repository); + + $messages = new MessageBag(); + $model = new Model('test-model'); + $input = new Input($model, $messages, ['fabric_pattern' => 'non_existing']); + + $this->expectException(PatternNotFoundException::class); + + $processor->processInput($input); + } +} diff --git a/tests/Platform/Fabric/FabricPromptTest.php b/tests/Platform/Fabric/FabricPromptTest.php new file mode 100644 index 00000000..961541e3 --- /dev/null +++ b/tests/Platform/Fabric/FabricPromptTest.php @@ -0,0 +1,40 @@ + 'Test Author'] + ); + + self::assertSame('test_pattern', $prompt->getPattern()); + self::assertSame('# Test Content', $prompt->getContent()); + self::assertSame(['author' => 'Test Author'], $prompt->getMetadata()); + } + + #[Test] + public function constructionWithoutMetadata(): void + { + $prompt = new FabricPrompt('test_pattern', '# Test Content'); + + self::assertSame('test_pattern', $prompt->getPattern()); + self::assertSame('# Test Content', $prompt->getContent()); + self::assertSame([], $prompt->getMetadata()); + } +} diff --git a/tests/Platform/Fabric/FabricRepositoryTest.php b/tests/Platform/Fabric/FabricRepositoryTest.php new file mode 100644 index 00000000..7652013c --- /dev/null +++ b/tests/Platform/Fabric/FabricRepositoryTest.php @@ -0,0 +1,171 @@ +testPatternsPath = sys_get_temp_dir().'/fabric-test-'.uniqid(); + mkdir($this->testPatternsPath); + } + + protected function tearDown(): void + { + $this->removeDirectory($this->testPatternsPath); + } + + #[Test] + public function constructorThrowsExceptionWhenPackageNotInstalled(): void + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Fabric patterns not found. Please install the "php-llm/fabric-pattern" package'); + + new FabricRepository(); + } + + #[Test] + public function loadExistingPattern(): void + { + $this->createTestPattern('test_pattern', '# Test Pattern Content'); + + $repository = new FabricRepository($this->testPatternsPath); + $prompt = $repository->load('test_pattern'); + + self::assertInstanceOf(FabricPrompt::class, $prompt); + self::assertSame('test_pattern', $prompt->getPattern()); + self::assertSame('# Test Pattern Content', $prompt->getContent()); + self::assertSame([], $prompt->getMetadata()); + } + + #[Test] + public function loadPatternWithMetadata(): void + { + $this->createTestPattern('test_pattern', '# Test Pattern Content'); + file_put_contents( + $this->testPatternsPath.'/test_pattern/README.md', + 'This is a readme' + ); + file_put_contents( + $this->testPatternsPath.'/test_pattern/metadata.json', + json_encode(['author' => 'Test Author', 'version' => '1.0']) + ); + + $repository = new FabricRepository($this->testPatternsPath); + $prompt = $repository->load('test_pattern'); + + $metadata = $prompt->getMetadata(); + self::assertSame('This is a readme', $metadata['readme']); + self::assertSame('Test Author', $metadata['author']); + self::assertSame('1.0', $metadata['version']); + } + + #[Test] + public function loadNonExistingPattern(): void + { + $repository = new FabricRepository($this->testPatternsPath); + + $this->expectException(PatternNotFoundException::class); + $this->expectExceptionMessage('Pattern "non_existing" not found'); + + $repository->load('non_existing'); + } + + #[Test] + public function loadUsesCache(): void + { + $this->createTestPattern('cached_pattern', 'Original content'); + + $repository = new FabricRepository($this->testPatternsPath); + $prompt1 = $repository->load('cached_pattern'); + + // Change the file content + file_put_contents( + $this->testPatternsPath.'/cached_pattern/system.md', + 'Changed content' + ); + + // Should still return cached version + $prompt2 = $repository->load('cached_pattern'); + self::assertSame($prompt1, $prompt2); + self::assertSame('Original content', $prompt2->getContent()); + } + + #[Test] + public function listPatterns(): void + { + $this->createTestPattern('pattern_a', 'Content A'); + $this->createTestPattern('pattern_b', 'Content B'); + $this->createTestPattern('pattern_c', 'Content C'); + mkdir($this->testPatternsPath.'/invalid_pattern'); // No system.md + + $repository = new FabricRepository($this->testPatternsPath); + $patterns = $repository->listPatterns(); + + self::assertSame(['pattern_a', 'pattern_b', 'pattern_c'], $patterns); + } + + #[Test] + public function listPatternsEmptyDirectory(): void + { + $repository = new FabricRepository($this->testPatternsPath); + $patterns = $repository->listPatterns(); + + self::assertSame([], $patterns); + } + + #[Test] + public function listPatternsNonExistingDirectory(): void + { + $repository = new FabricRepository('/non/existing/path'); + $patterns = $repository->listPatterns(); + + self::assertSame([], $patterns); + } + + #[Test] + public function exists(): void + { + $this->createTestPattern('existing_pattern', 'Content'); + + $repository = new FabricRepository($this->testPatternsPath); + + self::assertTrue($repository->exists('existing_pattern')); + self::assertFalse($repository->exists('non_existing')); + } + + private function createTestPattern(string $pattern, string $content): void + { + $patternPath = $this->testPatternsPath.'/'.$pattern; + mkdir($patternPath, 0777, true); + file_put_contents($patternPath.'/system.md', $content); + } + + private function removeDirectory(string $dir): void + { + if (!is_dir($dir)) { + return; + } + + $files = array_diff(scandir($dir) ?: [], ['.', '..']); + foreach ($files as $file) { + $path = $dir.'/'.$file; + is_dir($path) ? $this->removeDirectory($path) : unlink($path); + } + rmdir($dir); + } +} diff --git a/tests/Platform/Message/MessageFabricTest.php b/tests/Platform/Message/MessageFabricTest.php new file mode 100644 index 00000000..f850adb4 --- /dev/null +++ b/tests/Platform/Message/MessageFabricTest.php @@ -0,0 +1,55 @@ +testPatternsPath = sys_get_temp_dir().'/fabric-test-'.uniqid(); + mkdir($this->testPatternsPath.'/test_pattern', 0777, true); + file_put_contents( + $this->testPatternsPath.'/test_pattern/system.md', + '# Test Fabric Pattern' + ); + } + + protected function tearDown(): void + { + unlink($this->testPatternsPath.'/test_pattern/system.md'); + rmdir($this->testPatternsPath.'/test_pattern'); + rmdir($this->testPatternsPath); + } + + #[Test] + public function fabricMethodCreatesSystemMessage(): void + { + $message = Message::fabric('test_pattern', $this->testPatternsPath); + + self::assertInstanceOf(SystemMessage::class, $message); + self::assertSame('# Test Fabric Pattern', $message->content); + } + + #[Test] + public function fabricMethodThrowsExceptionForNonExistingPattern(): void + { + $this->expectException(PatternNotFoundException::class); + $this->expectExceptionMessage('Pattern "non_existing" not found'); + + Message::fabric('non_existing', $this->testPatternsPath); + } +} From 4960e82a88856f6f34182025404ad8b832112783 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Sun, 29 Jun 2025 23:57:56 +0200 Subject: [PATCH 02/10] fix --- examples/fabric/summarize.php | 39 ++++++++++---------- examples/fabric/with-processor.php | 37 +++++++++---------- src/Platform/Fabric/FabricInputProcessor.php | 7 +--- src/Platform/Fabric/FabricRepository.php | 4 +- 4 files changed, 40 insertions(+), 47 deletions(-) diff --git a/examples/fabric/summarize.php b/examples/fabric/summarize.php index 824251a4..ec314ef7 100644 --- a/examples/fabric/summarize.php +++ b/examples/fabric/summarize.php @@ -11,7 +11,7 @@ require_once dirname(__DIR__).'/../vendor/autoload.php'; if (empty($_ENV['OPENAI_API_KEY'])) { - echo 'Please set the OPENAI_API_KEY environment variable.'.PHP_EOL; + echo 'Please set the OPENAI_API_KEY environment variable.'.\PHP_EOL; exit(1); } @@ -24,17 +24,17 @@ // Example article to summarize $article = <<<'ARTICLE' - The field of artificial intelligence has undergone dramatic transformations in recent years, - with large language models (LLMs) emerging as one of the most significant breakthroughs. - These models, trained on vast amounts of text data, have demonstrated remarkable capabilities - in understanding and generating human-like text. The implications for software development, - content creation, and human-computer interaction are profound. - - However, with these advances come important considerations regarding ethics, bias, and the - responsible deployment of AI systems. Researchers and practitioners must work together to - ensure that these powerful tools are used in ways that benefit society while minimizing - potential harms. - ARTICLE; + The field of artificial intelligence has undergone dramatic transformations in recent years, + with large language models (LLMs) emerging as one of the most significant breakthroughs. + These models, trained on vast amounts of text data, have demonstrated remarkable capabilities + in understanding and generating human-like text. The implications for software development, + content creation, and human-computer interaction are profound. + + However, with these advances come important considerations regarding ethics, bias, and the + responsible deployment of AI systems. Researchers and practitioners must work together to + ensure that these powerful tools are used in ways that benefit society while minimizing + potential harms. + ARTICLE; // Create messages using Fabric pattern $messages = new MessageBag( @@ -45,15 +45,14 @@ // Call the chain $response = $chain->call($messages); - echo "Summary using Fabric pattern:".PHP_EOL; - echo "==============================".PHP_EOL; - echo $response->getContent().PHP_EOL; - -} catch (\RuntimeException $e) { + echo 'Summary using Fabric pattern:'.\PHP_EOL; + echo '=============================='.\PHP_EOL; + echo $response->getContent().\PHP_EOL; +} catch (RuntimeException $e) { if (str_contains($e->getMessage(), 'php-llm/fabric-pattern')) { - echo "Fabric patterns are not installed.".PHP_EOL; - echo "Please install them with: composer require php-llm/fabric-pattern".PHP_EOL; + echo 'Fabric patterns are not installed.'.\PHP_EOL; + echo 'Please install them with: composer require php-llm/fabric-pattern'.\PHP_EOL; exit(1); } throw $e; -} \ No newline at end of file +} diff --git a/examples/fabric/with-processor.php b/examples/fabric/with-processor.php index 946ed43e..4818f7b0 100644 --- a/examples/fabric/with-processor.php +++ b/examples/fabric/with-processor.php @@ -12,7 +12,7 @@ require_once dirname(__DIR__).'/../vendor/autoload.php'; if (empty($_ENV['OPENAI_API_KEY'])) { - echo 'Please set the OPENAI_API_KEY environment variable.'.PHP_EOL; + echo 'Please set the OPENAI_API_KEY environment variable.'.\PHP_EOL; exit(1); } @@ -20,40 +20,39 @@ // Initialize platform and model $platform = PlatformFactory::create($_ENV['OPENAI_API_KEY']); $model = new GPT(GPT::GPT_4O_MINI); - + // Create chain with Fabric processor $processor = new FabricInputProcessor(); $chain = new Chain($platform, $model, [$processor]); // Example code to analyze $code = <<<'CODE' - function processUserData($data) { - $sql = "SELECT * FROM users WHERE id = " . $data['id']; - $result = mysql_query($sql); - - while ($row = mysql_fetch_array($result)) { - echo $row['name'] . " - " . $row['email']; + function processUserData($data) { + $sql = "SELECT * FROM users WHERE id = " . $data['id']; + $result = mysql_query($sql); + + while ($row = mysql_fetch_array($result)) { + echo $row['name'] . " - " . $row['email']; + } } - } - CODE; + CODE; // Create messages $messages = new MessageBag( - Message::ofUser("Analyze this PHP code for security issues:\n\n" . $code) + Message::ofUser("Analyze this PHP code for security issues:\n\n".$code) ); // Call with Fabric pattern $response = $chain->call($messages, ['fabric_pattern' => 'analyze_code']); - echo "Code Analysis using Fabric pattern:".PHP_EOL; - echo "===================================".PHP_EOL; - echo $response->getContent().PHP_EOL; - -} catch (\RuntimeException $e) { + echo 'Code Analysis using Fabric pattern:'.\PHP_EOL; + echo '==================================='.\PHP_EOL; + echo $response->getContent().\PHP_EOL; +} catch (RuntimeException $e) { if (str_contains($e->getMessage(), 'php-llm/fabric-pattern')) { - echo "Fabric patterns are not installed.".PHP_EOL; - echo "Please install them with: composer require php-llm/fabric-pattern".PHP_EOL; + echo 'Fabric patterns are not installed.'.\PHP_EOL; + echo 'Please install them with: composer require php-llm/fabric-pattern'.\PHP_EOL; exit(1); } throw $e; -} \ No newline at end of file +} diff --git a/src/Platform/Fabric/FabricInputProcessor.php b/src/Platform/Fabric/FabricInputProcessor.php index 0af0bdc8..6b60694a 100644 --- a/src/Platform/Fabric/FabricInputProcessor.php +++ b/src/Platform/Fabric/FabricInputProcessor.php @@ -16,13 +16,10 @@ * This processor allows adding Fabric patterns through options: * - fabric_pattern: string - The pattern name to load */ -final class FabricInputProcessor implements InputProcessorInterface +final readonly class FabricInputProcessor implements InputProcessorInterface { - private ?FabricRepository $repository; - - public function __construct(?FabricRepository $repository = null) + public function __construct(private ?FabricRepository $repository = null) { - $this->repository = $repository; } public function processInput(Input $input): void diff --git a/src/Platform/Fabric/FabricRepository.php b/src/Platform/Fabric/FabricRepository.php index 8e3cfe83..4216dca7 100644 --- a/src/Platform/Fabric/FabricRepository.php +++ b/src/Platform/Fabric/FabricRepository.php @@ -26,9 +26,7 @@ public function __construct(?string $patternsPath = null) if (is_dir($fabricPatternPath)) { $this->patternsPath = $fabricPatternPath; } else { - throw new \RuntimeException( - 'Fabric patterns not found. Please install the "php-llm/fabric-pattern" package: composer require php-llm/fabric-pattern' - ); + throw new \RuntimeException('Fabric patterns not found. Please install the "php-llm/fabric-pattern" package: composer require php-llm/fabric-pattern'); } } else { $this->patternsPath = $patternsPath; From d7127e5091be792f7465aa1831cec24752661cbe Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 30 Jun 2025 00:00:19 +0200 Subject: [PATCH 03/10] chore: add php-llm/fabric-pattern to require-dev and suggest --- composer.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composer.json b/composer.json index 24604bc6..250dfd0f 100644 --- a/composer.json +++ b/composer.json @@ -46,6 +46,7 @@ "mrmysql/youtube-transcript": "^v0.0.5", "php-cs-fixer/shim": "^3.70", "php-http/discovery": "^1.20", + "php-llm/fabric-pattern": "^0.1 || ^1.0", "phpstan/phpstan": "^2.0", "phpstan/phpstan-symfony": "^2.0", "phpstan/phpstan-webmozart-assert": "^2.0", @@ -70,6 +71,7 @@ "doctrine/dbal": "For using MariaDB via Doctrine as retrieval vector store", "mongodb/mongodb": "For using MongoDB Atlas as retrieval vector store.", "mrmysql/youtube-transcript": "For using the YouTube transcription tool.", + "php-llm/fabric-pattern": "For using Fabric patterns - a collection of pre-built system prompts.", "probots-io/pinecone-php": "For using the Pinecone as retrieval vector store.", "symfony/dom-crawler": "For using the Crawler tool." }, From 006b7040f3da7f47f233c5f41d57fe3c06897283 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 30 Jun 2025 00:01:34 +0200 Subject: [PATCH 04/10] refactor: simplify fabric examples with directory check - Remove try/catch blocks for cleaner code - Use simple directory existence check - Update composer.json to only allow fabric-pattern ^0.1 --- composer.json | 2 +- examples/fabric/summarize.php | 79 ++++++++++++++---------------- examples/fabric/with-processor.php | 70 +++++++++++++------------- 3 files changed, 73 insertions(+), 78 deletions(-) diff --git a/composer.json b/composer.json index 250dfd0f..a915ff48 100644 --- a/composer.json +++ b/composer.json @@ -46,7 +46,7 @@ "mrmysql/youtube-transcript": "^v0.0.5", "php-cs-fixer/shim": "^3.70", "php-http/discovery": "^1.20", - "php-llm/fabric-pattern": "^0.1 || ^1.0", + "php-llm/fabric-pattern": "^0.1", "phpstan/phpstan": "^2.0", "phpstan/phpstan-symfony": "^2.0", "phpstan/phpstan-webmozart-assert": "^2.0", diff --git a/examples/fabric/summarize.php b/examples/fabric/summarize.php index ec314ef7..76fbab8f 100644 --- a/examples/fabric/summarize.php +++ b/examples/fabric/summarize.php @@ -11,48 +11,45 @@ require_once dirname(__DIR__).'/../vendor/autoload.php'; if (empty($_ENV['OPENAI_API_KEY'])) { - echo 'Please set the OPENAI_API_KEY environment variable.'.\PHP_EOL; + echo 'Please set the OPENAI_API_KEY environment variable.'.PHP_EOL; exit(1); } -// Check if Fabric patterns are available -try { - // Initialize platform and model - $platform = PlatformFactory::create($_ENV['OPENAI_API_KEY']); - $model = new GPT(GPT::GPT_4O_MINI); - $chain = new Chain($platform, $model); - - // Example article to summarize - $article = <<<'ARTICLE' - The field of artificial intelligence has undergone dramatic transformations in recent years, - with large language models (LLMs) emerging as one of the most significant breakthroughs. - These models, trained on vast amounts of text data, have demonstrated remarkable capabilities - in understanding and generating human-like text. The implications for software development, - content creation, and human-computer interaction are profound. - - However, with these advances come important considerations regarding ethics, bias, and the - responsible deployment of AI systems. Researchers and practitioners must work together to - ensure that these powerful tools are used in ways that benefit society while minimizing - potential harms. - ARTICLE; - - // Create messages using Fabric pattern - $messages = new MessageBag( - Message::fabric('create_summary'), - Message::ofUser($article) - ); - - // Call the chain - $response = $chain->call($messages); - - echo 'Summary using Fabric pattern:'.\PHP_EOL; - echo '=============================='.\PHP_EOL; - echo $response->getContent().\PHP_EOL; -} catch (RuntimeException $e) { - if (str_contains($e->getMessage(), 'php-llm/fabric-pattern')) { - echo 'Fabric patterns are not installed.'.\PHP_EOL; - echo 'Please install them with: composer require php-llm/fabric-pattern'.\PHP_EOL; - exit(1); - } - throw $e; +// Check if Fabric patterns package is installed +if (!is_dir(dirname(__DIR__, 2).'/vendor/php-llm/fabric-pattern')) { + echo 'Fabric patterns are not installed.'.PHP_EOL; + echo 'Please install them with: composer require php-llm/fabric-pattern'.PHP_EOL; + exit(1); } + +// Initialize platform and model +$platform = PlatformFactory::create($_ENV['OPENAI_API_KEY']); +$model = new GPT(GPT::GPT_4O_MINI); +$chain = new Chain($platform, $model); + +// Example article to summarize +$article = <<<'ARTICLE' + The field of artificial intelligence has undergone dramatic transformations in recent years, + with large language models (LLMs) emerging as one of the most significant breakthroughs. + These models, trained on vast amounts of text data, have demonstrated remarkable capabilities + in understanding and generating human-like text. The implications for software development, + content creation, and human-computer interaction are profound. + + However, with these advances come important considerations regarding ethics, bias, and the + responsible deployment of AI systems. Researchers and practitioners must work together to + ensure that these powerful tools are used in ways that benefit society while minimizing + potential harms. + ARTICLE; + +// Create messages using Fabric pattern +$messages = new MessageBag( + Message::fabric('create_summary'), + Message::ofUser($article) +); + +// Call the chain +$response = $chain->call($messages); + +echo 'Summary using Fabric pattern:'.PHP_EOL; +echo '=============================='.PHP_EOL; +echo $response->getContent().PHP_EOL; \ No newline at end of file diff --git a/examples/fabric/with-processor.php b/examples/fabric/with-processor.php index 4818f7b0..47532ca1 100644 --- a/examples/fabric/with-processor.php +++ b/examples/fabric/with-processor.php @@ -12,47 +12,45 @@ require_once dirname(__DIR__).'/../vendor/autoload.php'; if (empty($_ENV['OPENAI_API_KEY'])) { - echo 'Please set the OPENAI_API_KEY environment variable.'.\PHP_EOL; + echo 'Please set the OPENAI_API_KEY environment variable.'.PHP_EOL; exit(1); } -try { - // Initialize platform and model - $platform = PlatformFactory::create($_ENV['OPENAI_API_KEY']); - $model = new GPT(GPT::GPT_4O_MINI); +// Check if Fabric patterns package is installed +if (!is_dir(dirname(__DIR__, 2).'/vendor/php-llm/fabric-pattern')) { + echo 'Fabric patterns are not installed.'.PHP_EOL; + echo 'Please install them with: composer require php-llm/fabric-pattern'.PHP_EOL; + exit(1); +} - // Create chain with Fabric processor - $processor = new FabricInputProcessor(); - $chain = new Chain($platform, $model, [$processor]); +// Initialize platform and model +$platform = PlatformFactory::create($_ENV['OPENAI_API_KEY']); +$model = new GPT(GPT::GPT_4O_MINI); - // Example code to analyze - $code = <<<'CODE' - function processUserData($data) { - $sql = "SELECT * FROM users WHERE id = " . $data['id']; - $result = mysql_query($sql); +// Create chain with Fabric processor +$processor = new FabricInputProcessor(); +$chain = new Chain($platform, $model, [$processor]); - while ($row = mysql_fetch_array($result)) { - echo $row['name'] . " - " . $row['email']; - } +// Example code to analyze +$code = <<<'CODE' + function processUserData($data) { + $sql = "SELECT * FROM users WHERE id = " . $data['id']; + $result = mysql_query($sql); + + while ($row = mysql_fetch_array($result)) { + echo $row['name'] . " - " . $row['email']; } - CODE; - - // Create messages - $messages = new MessageBag( - Message::ofUser("Analyze this PHP code for security issues:\n\n".$code) - ); - - // Call with Fabric pattern - $response = $chain->call($messages, ['fabric_pattern' => 'analyze_code']); - - echo 'Code Analysis using Fabric pattern:'.\PHP_EOL; - echo '==================================='.\PHP_EOL; - echo $response->getContent().\PHP_EOL; -} catch (RuntimeException $e) { - if (str_contains($e->getMessage(), 'php-llm/fabric-pattern')) { - echo 'Fabric patterns are not installed.'.\PHP_EOL; - echo 'Please install them with: composer require php-llm/fabric-pattern'.\PHP_EOL; - exit(1); } - throw $e; -} + CODE; + +// Create messages +$messages = new MessageBag( + Message::ofUser("Analyze this PHP code for security issues:\n\n".$code) +); + +// Call with Fabric pattern +$response = $chain->call($messages, ['fabric_pattern' => 'analyze_code']); + +echo 'Code Analysis using Fabric pattern:'.PHP_EOL; +echo '==================================='.PHP_EOL; +echo $response->getContent().PHP_EOL; \ No newline at end of file From d46f35717627570e23651391e2ed33392cfe726f Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 30 Jun 2025 00:02:08 +0200 Subject: [PATCH 05/10] feat: add fabric pattern names to example outputs --- examples/fabric/summarize.php | 4 ++-- examples/fabric/with-processor.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/fabric/summarize.php b/examples/fabric/summarize.php index 76fbab8f..7751c418 100644 --- a/examples/fabric/summarize.php +++ b/examples/fabric/summarize.php @@ -50,6 +50,6 @@ // Call the chain $response = $chain->call($messages); -echo 'Summary using Fabric pattern:'.PHP_EOL; -echo '=============================='.PHP_EOL; +echo 'Summary using Fabric pattern "create_summary":'.PHP_EOL; +echo '=============================================='.PHP_EOL; echo $response->getContent().PHP_EOL; \ No newline at end of file diff --git a/examples/fabric/with-processor.php b/examples/fabric/with-processor.php index 47532ca1..a2ae253e 100644 --- a/examples/fabric/with-processor.php +++ b/examples/fabric/with-processor.php @@ -51,6 +51,6 @@ function processUserData($data) { // Call with Fabric pattern $response = $chain->call($messages, ['fabric_pattern' => 'analyze_code']); -echo 'Code Analysis using Fabric pattern:'.PHP_EOL; -echo '==================================='.PHP_EOL; +echo 'Code Analysis using Fabric pattern "analyze_code":'.PHP_EOL; +echo '=================================================='.PHP_EOL; echo $response->getContent().PHP_EOL; \ No newline at end of file From b89571e7022c472e222990c3d2c9c7e00790ff68 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 30 Jun 2025 00:04:16 +0200 Subject: [PATCH 06/10] style: add missing newlines at end of example files --- examples/fabric/summarize.php | 2 +- examples/fabric/with-processor.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/fabric/summarize.php b/examples/fabric/summarize.php index 7751c418..e143f804 100644 --- a/examples/fabric/summarize.php +++ b/examples/fabric/summarize.php @@ -52,4 +52,4 @@ echo 'Summary using Fabric pattern "create_summary":'.PHP_EOL; echo '=============================================='.PHP_EOL; -echo $response->getContent().PHP_EOL; \ No newline at end of file +echo $response->getContent().PHP_EOL; diff --git a/examples/fabric/with-processor.php b/examples/fabric/with-processor.php index a2ae253e..51744dad 100644 --- a/examples/fabric/with-processor.php +++ b/examples/fabric/with-processor.php @@ -53,4 +53,4 @@ function processUserData($data) { echo 'Code Analysis using Fabric pattern "analyze_code":'.PHP_EOL; echo '=================================================='.PHP_EOL; -echo $response->getContent().PHP_EOL; \ No newline at end of file +echo $response->getContent().PHP_EOL; From 54e4a6d5768cdb3ec26167b75076d256217bbc32 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 30 Jun 2025 00:04:52 +0200 Subject: [PATCH 07/10] refactor: replace isset with array_key_exists --- src/Platform/Fabric/FabricInputProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Platform/Fabric/FabricInputProcessor.php b/src/Platform/Fabric/FabricInputProcessor.php index 6b60694a..64aac861 100644 --- a/src/Platform/Fabric/FabricInputProcessor.php +++ b/src/Platform/Fabric/FabricInputProcessor.php @@ -26,7 +26,7 @@ public function processInput(Input $input): void { $options = $input->getOptions(); - if (!isset($options['fabric_pattern'])) { + if (!\array_key_exists('fabric_pattern', $options)) { return; } From 87956d21f6c908013b37b30eedd479d996b5ad7c Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 30 Jun 2025 00:05:59 +0200 Subject: [PATCH 08/10] refactor: make FabricRepository mandatory in FabricInputProcessor - Repository is now always initialized (either provided or created) - Simplified code by removing null coalescing in processInput --- src/Platform/Fabric/FabricInputProcessor.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Platform/Fabric/FabricInputProcessor.php b/src/Platform/Fabric/FabricInputProcessor.php index 64aac861..85834de2 100644 --- a/src/Platform/Fabric/FabricInputProcessor.php +++ b/src/Platform/Fabric/FabricInputProcessor.php @@ -18,8 +18,11 @@ */ final readonly class FabricInputProcessor implements InputProcessorInterface { - public function __construct(private ?FabricRepository $repository = null) + private FabricRepository $repository; + + public function __construct(?FabricRepository $repository = null) { + $this->repository = $repository ?? new FabricRepository(); } public function processInput(Input $input): void @@ -36,8 +39,7 @@ public function processInput(Input $input): void } // Load the pattern and prepend as system message - $repository = $this->repository ?? new FabricRepository(); - $fabricPrompt = $repository->load($pattern); + $fabricPrompt = $this->repository->load($pattern); $systemMessage = new SystemMessage($fabricPrompt->getContent()); // Prepend the system message From 33deeda50981bfcfa7d9a836af9f128fb84107e3 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 30 Jun 2025 00:06:59 +0200 Subject: [PATCH 09/10] feat: add non-empty-string type annotation for pattern - FabricPromptInterface::getPattern() now returns non-empty-string - FabricPrompt constructor expects non-empty-string for pattern - Improves type safety and PHPStan analysis --- src/Platform/Fabric/FabricPrompt.php | 4 ++++ src/Platform/Fabric/FabricPromptInterface.php | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/Platform/Fabric/FabricPrompt.php b/src/Platform/Fabric/FabricPrompt.php index a55b8c1b..79947dad 100644 --- a/src/Platform/Fabric/FabricPrompt.php +++ b/src/Platform/Fabric/FabricPrompt.php @@ -10,6 +10,7 @@ final readonly class FabricPrompt implements FabricPromptInterface { /** + * @param non-empty-string $pattern * @param array $metadata */ public function __construct( @@ -19,6 +20,9 @@ public function __construct( ) { } + /** + * @return non-empty-string + */ public function getPattern(): string { return $this->pattern; diff --git a/src/Platform/Fabric/FabricPromptInterface.php b/src/Platform/Fabric/FabricPromptInterface.php index 4c74eb3b..4406e8ef 100644 --- a/src/Platform/Fabric/FabricPromptInterface.php +++ b/src/Platform/Fabric/FabricPromptInterface.php @@ -11,6 +11,8 @@ interface FabricPromptInterface { /** * Get the pattern name (e.g., 'create_summary'). + * + * @return non-empty-string */ public function getPattern(): string; From 9d0476af1ee9703409be5bc6ee4955365289d9b2 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 30 Jun 2025 00:08:45 +0200 Subject: [PATCH 10/10] - --- examples/fabric/summarize.php | 12 ++++++------ examples/fabric/with-processor.php | 12 ++++++------ src/Platform/Fabric/FabricInputProcessor.php | 8 +++----- src/Platform/Fabric/FabricPrompt.php | 2 +- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/examples/fabric/summarize.php b/examples/fabric/summarize.php index e143f804..3aa31721 100644 --- a/examples/fabric/summarize.php +++ b/examples/fabric/summarize.php @@ -11,14 +11,14 @@ require_once dirname(__DIR__).'/../vendor/autoload.php'; if (empty($_ENV['OPENAI_API_KEY'])) { - echo 'Please set the OPENAI_API_KEY environment variable.'.PHP_EOL; + echo 'Please set the OPENAI_API_KEY environment variable.'.\PHP_EOL; exit(1); } // Check if Fabric patterns package is installed if (!is_dir(dirname(__DIR__, 2).'/vendor/php-llm/fabric-pattern')) { - echo 'Fabric patterns are not installed.'.PHP_EOL; - echo 'Please install them with: composer require php-llm/fabric-pattern'.PHP_EOL; + echo 'Fabric patterns are not installed.'.\PHP_EOL; + echo 'Please install them with: composer require php-llm/fabric-pattern'.\PHP_EOL; exit(1); } @@ -50,6 +50,6 @@ // Call the chain $response = $chain->call($messages); -echo 'Summary using Fabric pattern "create_summary":'.PHP_EOL; -echo '=============================================='.PHP_EOL; -echo $response->getContent().PHP_EOL; +echo 'Summary using Fabric pattern "create_summary":'.\PHP_EOL; +echo '=============================================='.\PHP_EOL; +echo $response->getContent().\PHP_EOL; diff --git a/examples/fabric/with-processor.php b/examples/fabric/with-processor.php index 51744dad..dc5020da 100644 --- a/examples/fabric/with-processor.php +++ b/examples/fabric/with-processor.php @@ -12,14 +12,14 @@ require_once dirname(__DIR__).'/../vendor/autoload.php'; if (empty($_ENV['OPENAI_API_KEY'])) { - echo 'Please set the OPENAI_API_KEY environment variable.'.PHP_EOL; + echo 'Please set the OPENAI_API_KEY environment variable.'.\PHP_EOL; exit(1); } // Check if Fabric patterns package is installed if (!is_dir(dirname(__DIR__, 2).'/vendor/php-llm/fabric-pattern')) { - echo 'Fabric patterns are not installed.'.PHP_EOL; - echo 'Please install them with: composer require php-llm/fabric-pattern'.PHP_EOL; + echo 'Fabric patterns are not installed.'.\PHP_EOL; + echo 'Please install them with: composer require php-llm/fabric-pattern'.\PHP_EOL; exit(1); } @@ -51,6 +51,6 @@ function processUserData($data) { // Call with Fabric pattern $response = $chain->call($messages, ['fabric_pattern' => 'analyze_code']); -echo 'Code Analysis using Fabric pattern "analyze_code":'.PHP_EOL; -echo '=================================================='.PHP_EOL; -echo $response->getContent().PHP_EOL; +echo 'Code Analysis using Fabric pattern "analyze_code":'.\PHP_EOL; +echo '=================================================='.\PHP_EOL; +echo $response->getContent().\PHP_EOL; diff --git a/src/Platform/Fabric/FabricInputProcessor.php b/src/Platform/Fabric/FabricInputProcessor.php index 85834de2..bdd0461b 100644 --- a/src/Platform/Fabric/FabricInputProcessor.php +++ b/src/Platform/Fabric/FabricInputProcessor.php @@ -18,11 +18,9 @@ */ final readonly class FabricInputProcessor implements InputProcessorInterface { - private FabricRepository $repository; - - public function __construct(?FabricRepository $repository = null) - { - $this->repository = $repository ?? new FabricRepository(); + public function __construct( + private FabricRepository $repository = new FabricRepository(), + ) { } public function processInput(Input $input): void diff --git a/src/Platform/Fabric/FabricPrompt.php b/src/Platform/Fabric/FabricPrompt.php index 79947dad..6d28303b 100644 --- a/src/Platform/Fabric/FabricPrompt.php +++ b/src/Platform/Fabric/FabricPrompt.php @@ -10,7 +10,7 @@ final readonly class FabricPrompt implements FabricPromptInterface { /** - * @param non-empty-string $pattern + * @param non-empty-string $pattern * @param array $metadata */ public function __construct(