From 6d1236580c65e2b94ad0c9e4b200d85839c208a4 Mon Sep 17 00:00:00 2001 From: Muhammad Yahaya Date: Sun, 17 Aug 2025 14:32:19 +0100 Subject: [PATCH 1/2] feat: Add markdownConverter method to Wetrocloud class and update composer.json keywords --- composer.json | 3 +++ src/Wetrocloud.php | 29 ++++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 632ab8f..00fbcfe 100644 --- a/composer.json +++ b/composer.json @@ -5,10 +5,13 @@ "license": "MIT", "keywords": [ "wetrocloud", + "RAG", + "Data Extraction", "php", "sdk", "wetrocloud sdk", "wetrocloud api" + ], "autoload": { "psr-4": { diff --git a/src/Wetrocloud.php b/src/Wetrocloud.php index 50bfd89..6646f7d 100644 --- a/src/Wetrocloud.php +++ b/src/Wetrocloud.php @@ -357,8 +357,8 @@ public function textGeneration(array $messages, string $model): array * @throws \RuntimeException * **/ - public function imageToText(string $imageUrl, string $requestQuery): array - { + public function imageToText(string $imageUrl, string $requestQuery): array + { try { $payload = [ 'image_url' => $imageUrl, @@ -370,7 +370,6 @@ public function imageToText(string $imageUrl, string $requestQuery): array ]); return $this->decodeResponse($response); - } catch (\JsonException $e) { throw new \RuntimeException("Failed to encode messages: " . $e->getMessage()); } catch (GuzzleException $e) { @@ -378,5 +377,29 @@ public function imageToText(string $imageUrl, string $requestQuery): array } } + /** + * Convert a resource (file, web, image) to Markdown + * + * @param string $resource The resource URL (file, web page, or image) + * @param string $resourceType The type of resource: "file", "web", or "image" + * @return array Response from the API containing response, tokens, and success status + * @throws \RuntimeException + */ + public function markdownConverter(string $resource, string $resourceType): array + { + try { + $payload = [ + 'link' => $resource, + 'resource_type' => $resourceType, + ]; + + $response = $this->client->post('/v2/markdown-converter/', [ + 'json' => $payload, + ]); + return $this->decodeResponse($response); + } catch (GuzzleException $e) { + throw new \RuntimeException("Failed to convert resource to Markdown: " . $e->getMessage()); + } + } } From 8f46a9485ec7752d7d90282a0f29853e1ccc167f Mon Sep 17 00:00:00 2001 From: Muhammad Yahaya Date: Sun, 17 Aug 2025 22:25:47 +0100 Subject: [PATCH 2/2] test: Enhance WetrocloudSdkTest with new text generation, image-to-text, and markdown conversion tests --- tests/Feature/WetrocloudSdkTest.php | 282 +++++++++++++++++++++------- 1 file changed, 213 insertions(+), 69 deletions(-) diff --git a/tests/Feature/WetrocloudSdkTest.php b/tests/Feature/WetrocloudSdkTest.php index be7dd26..aad9e5c 100644 --- a/tests/Feature/WetrocloudSdkTest.php +++ b/tests/Feature/WetrocloudSdkTest.php @@ -9,20 +9,20 @@ // Test constructor and basic setup test('can create wetrocloud instance with api key', function () { $wetrocloud = new Wetrocloud('test-api-key'); - + expect($wetrocloud)->toBeInstanceOf(Wetrocloud::class); expect($wetrocloud->getBaseUrl())->toBe('https://api.wetrocloud.com'); }); test('can create wetrocloud instance with custom base url', function () { $wetrocloud = new Wetrocloud('test-api-key', 'https://custom-api.example.com'); - + expect($wetrocloud->getBaseUrl())->toBe('https://custom-api.example.com'); }); test('can get http client instance', function () { $wetrocloud = new Wetrocloud('test-api-key'); - + expect($wetrocloud->getClient())->toBeInstanceOf(Client::class); }); @@ -38,21 +38,21 @@ 'collection_id' => 'test-collection-123', 'message' => 'Collection created successfully' ])); - + $mockClient = Mockery::mock(Client::class); $mockClient->shouldReceive('post') ->with('/v1/collection/create/', ['json' => []]) ->once() ->andReturn($mockResponse); - + $wetrocloud = new Wetrocloud('test-api-key'); $wetrocloudReflection = new ReflectionClass($wetrocloud); $clientProperty = $wetrocloudReflection->getProperty('client'); $clientProperty->setAccessible(true); $clientProperty->setValue($wetrocloud, $mockClient); - + $result = $wetrocloud->createCollection(); - + expect($result)->toBe([ 'success' => true, 'collection_id' => 'test-collection-123', @@ -66,21 +66,21 @@ 'collection_id' => 'custom-collection-id', 'message' => 'Collection created successfully' ])); - + $mockClient = Mockery::mock(Client::class); $mockClient->shouldReceive('post') ->with('/v1/collection/create/', ['json' => ['collection_id' => 'custom-collection-id']]) ->once() ->andReturn($mockResponse); - + $wetrocloud = new Wetrocloud('test-api-key'); $wetrocloudReflection = new ReflectionClass($wetrocloud); $clientProperty = $wetrocloudReflection->getProperty('client'); $clientProperty->setAccessible(true); $clientProperty->setValue($wetrocloud, $mockClient); - + $result = $wetrocloud->createCollection('custom-collection-id'); - + expect($result)->toBe([ 'success' => true, 'collection_id' => 'custom-collection-id', @@ -97,21 +97,21 @@ ['id' => 'collection-2', 'name' => 'Test Collection 2'] ] ])); - + $mockClient = Mockery::mock(Client::class); $mockClient->shouldReceive('get') ->with('/v1/collection/all/') ->once() ->andReturn($mockResponse); - + $wetrocloud = new Wetrocloud('test-api-key'); $wetrocloudReflection = new ReflectionClass($wetrocloud); $clientProperty = $wetrocloudReflection->getProperty('client'); $clientProperty->setAccessible(true); $clientProperty->setValue($wetrocloud, $mockClient); - + $result = $wetrocloud->listAllCollections(); - + expect($result)->toBe([ 'success' => true, 'collections' => [ @@ -128,7 +128,7 @@ 'resource_id' => 'resource-123', 'message' => 'Resource inserted successfully' ])); - + $mockClient = Mockery::mock(Client::class); $mockClient->shouldReceive('post') ->with('/v1/resource/insert/', [ @@ -140,15 +140,15 @@ ]) ->once() ->andReturn($mockResponse); - + $wetrocloud = new Wetrocloud('test-api-key'); $wetrocloudReflection = new ReflectionClass($wetrocloud); $clientProperty = $wetrocloudReflection->getProperty('client'); $clientProperty->setAccessible(true); $clientProperty->setValue($wetrocloud, $mockClient); - + $result = $wetrocloud->insertResource('test-collection', 'Sample document content', 'document'); - + expect($result)->toBe([ 'success' => true, 'resource_id' => 'resource-123', @@ -163,7 +163,7 @@ 'results' => ['result1', 'result2'], 'query' => 'test query' ])); - + $mockClient = Mockery::mock(Client::class); $mockClient->shouldReceive('post') ->with('v1/collection/query/', [ @@ -176,15 +176,15 @@ ]) ->once() ->andReturn($mockResponse); - + $wetrocloud = new Wetrocloud('test-api-key'); $wetrocloudReflection = new ReflectionClass($wetrocloud); $clientProperty = $wetrocloudReflection->getProperty('client'); $clientProperty->setAccessible(true); $clientProperty->setValue($wetrocloud, $mockClient); - + $result = $wetrocloud->queryCollection('test-collection', 'test query'); - + expect($result)->toBe([ 'success' => true, 'results' => ['result1', 'result2'], @@ -198,7 +198,7 @@ 'results' => ['structured result'], 'query' => 'test query' ])); - + $mockClient = Mockery::mock(Client::class); $mockClient->shouldReceive('post') ->with('v1/collection/query/', [ @@ -211,20 +211,20 @@ ]) ->once() ->andReturn($mockResponse); - + $wetrocloud = new Wetrocloud('test-api-key'); $wetrocloudReflection = new ReflectionClass($wetrocloud); $clientProperty = $wetrocloudReflection->getProperty('client'); $clientProperty->setAccessible(true); $clientProperty->setValue($wetrocloud, $mockClient); - + $result = $wetrocloud->queryCollection( - 'test-collection', - 'test query', - '{"type": "object"}', + 'test-collection', + 'test query', + '{"type": "object"}', '{"required": ["name"]}' ); - + expect($result)->toBe([ 'success' => true, 'results' => ['structured result'], @@ -239,7 +239,7 @@ 'response' => 'Hello! How can I help you?', 'tokens' => 15 ])); - + $mockClient = Mockery::mock(Client::class); $mockClient->shouldReceive('post') ->with('/v1/collection/chat/', [ @@ -250,15 +250,15 @@ ]) ->once() ->andReturn($mockResponse); - + $wetrocloud = new Wetrocloud('test-api-key'); $wetrocloudReflection = new ReflectionClass($wetrocloud); $clientProperty = $wetrocloudReflection->getProperty('client'); $clientProperty->setAccessible(true); $clientProperty->setValue($wetrocloud, $mockClient); - + $result = $wetrocloud->chatCollection('test-collection', 'Hello'); - + expect($result)->toBe([ 'success' => true, 'response' => 'Hello! How can I help you?', @@ -272,12 +272,12 @@ 'response' => 'Based on our conversation...', 'tokens' => 25 ])); - + $chatHistory = json_encode([ ['role' => 'user', 'content' => 'Previous message'], ['role' => 'assistant', 'content' => 'Previous response'] ]); - + $mockClient = Mockery::mock(Client::class); $mockClient->shouldReceive('post') ->with('/v1/collection/chat/', [ @@ -289,15 +289,15 @@ ]) ->once() ->andReturn($mockResponse); - + $wetrocloud = new Wetrocloud('test-api-key'); $wetrocloudReflection = new ReflectionClass($wetrocloud); $clientProperty = $wetrocloudReflection->getProperty('client'); $clientProperty->setAccessible(true); $clientProperty->setValue($wetrocloud, $mockClient); - + $result = $wetrocloud->chatCollection('test-collection', 'Follow up question', $chatHistory); - + expect($result)->toBe([ 'success' => true, 'response' => 'Based on our conversation...', @@ -312,7 +312,7 @@ 'label' => 'Technology', 'tokens' => 10 ])); - + $mockClient = Mockery::mock(Client::class); $mockClient->shouldReceive('post') ->with('/v1/categorize/', [ @@ -326,13 +326,13 @@ ]) ->once() ->andReturn($mockResponse); - + $wetrocloud = new Wetrocloud('test-api-key'); $wetrocloudReflection = new ReflectionClass($wetrocloud); $clientProperty = $wetrocloudReflection->getProperty('client'); $clientProperty->setAccessible(true); $clientProperty->setValue($wetrocloud, $mockClient); - + $result = $wetrocloud->categorizeResource( 'This is a technology article about AI', 'article', @@ -340,7 +340,7 @@ 'Technology,Science,Health', 'Categorize this content' ); - + expect($result)->toBe([ 'success' => true, 'label' => 'Technology', @@ -354,7 +354,7 @@ 'success' => true, 'message' => 'Resource removed successfully' ])); - + $mockClient = Mockery::mock(Client::class); $mockClient->shouldReceive('delete') ->with('/v1/resource/remove/', [ @@ -365,15 +365,15 @@ ]) ->once() ->andReturn($mockResponse); - + $wetrocloud = new Wetrocloud('test-api-key'); $wetrocloudReflection = new ReflectionClass($wetrocloud); $clientProperty = $wetrocloudReflection->getProperty('client'); $clientProperty->setAccessible(true); $clientProperty->setValue($wetrocloud, $mockClient); - + $result = $wetrocloud->removeResource('test-collection', 'resource-123'); - + expect($result)->toBe([ 'success' => true, 'message' => 'Resource removed successfully' @@ -386,7 +386,7 @@ 'success' => true, 'message' => 'Collection deleted successfully' ])); - + $mockClient = Mockery::mock(Client::class); $mockClient->shouldReceive('delete') ->with('/v1/collection/delete/', [ @@ -396,55 +396,199 @@ ]) ->once() ->andReturn($mockResponse); - + $wetrocloud = new Wetrocloud('test-api-key'); $wetrocloudReflection = new ReflectionClass($wetrocloud); $clientProperty = $wetrocloudReflection->getProperty('client'); $clientProperty->setAccessible(true); $clientProperty->setValue($wetrocloud, $mockClient); - + $result = $wetrocloud->deleteCollection('test-collection'); - + expect($result)->toBe([ 'success' => true, 'message' => 'Collection deleted successfully' ]); }); -// Test error handling -test('handles invalid json response', function () { - $mockResponse = new Response(200, [], 'invalid json'); - +// Test textGeneration +test('can generate text without rag', function () { + $mockResponse = new Response(200, [], json_encode([ + 'success' => true, + 'response' => 'Generated text response', + 'tokens' => 50, + 'model' => 'llama-3.3-70b' + ])); + + $messages = [ + ['role' => 'user', 'content' => 'Hello, how are you?'], + ['role' => 'assistant', 'content' => 'I am doing well, thank you!'] + ]; + $mockClient = Mockery::mock(Client::class); - $mockClient->shouldReceive('get') - ->with('/v1/collection/all/') + $mockClient->shouldReceive('post') + ->with('/v1/text-generation/', [ + 'multipart' => [ + [ + 'name' => 'messages', + 'contents' => json_encode($messages, JSON_THROW_ON_ERROR), + ], + [ + 'name' => 'model', + 'contents' => 'llama-3.3-70b', + ], + ] + ]) ->once() ->andReturn($mockResponse); - + $wetrocloud = new Wetrocloud('test-api-key'); $wetrocloudReflection = new ReflectionClass($wetrocloud); $clientProperty = $wetrocloudReflection->getProperty('client'); $clientProperty->setAccessible(true); $clientProperty->setValue($wetrocloud, $mockClient); - - expect(fn() => $wetrocloud->listAllCollections()) - ->toThrow(\RuntimeException::class, 'Invalid API response: expected JSON object, got invalid json'); + + $result = $wetrocloud->textGeneration($messages, 'llama-3.3-70b'); + + expect($result)->toBe([ + 'success' => true, + 'response' => 'Generated text response', + 'tokens' => 50, + 'model' => 'llama-3.3-70b' + ]); }); -test('handles guzzle exception', function () { + +// Test imageToText method +test('can extract text from image', function () { + $mockResponse = new Response(200, [], json_encode([ + 'success' => true, + 'text' => 'Extracted text from image', + 'tokens' => 20 + ])); + $mockClient = Mockery::mock(Client::class); - $mockClient->shouldReceive('get') - ->with('/v1/collection/all/') + $mockClient->shouldReceive('post') + ->with('/v1/image-to-text/', [ + 'json' => [ + 'image_url' => 'https://example.com/image.jpg', + 'request_query' => 'What text is in this image?' + ] + ]) ->once() - ->andThrow(new \GuzzleHttp\Exception\ConnectException('Connection failed', new \GuzzleHttp\Psr7\Request('GET', 'test'))); - + ->andReturn($mockResponse); + $wetrocloud = new Wetrocloud('test-api-key'); $wetrocloudReflection = new ReflectionClass($wetrocloud); $clientProperty = $wetrocloudReflection->getProperty('client'); $clientProperty->setAccessible(true); $clientProperty->setValue($wetrocloud, $mockClient); - - expect(fn() => $wetrocloud->listAllCollections()) - ->toThrow(\RuntimeException::class, 'Failed to fetch collections: Connection failed'); + + $result = $wetrocloud->imageToText('https://example.com/image.jpg', 'What text is in this image?'); + + expect($result)->toBe([ + 'success' => true, + 'text' => 'Extracted text from image', + 'tokens' => 20 + ]); +}); + +// Test markdownConverter +test('can convert file to markdown', function () { + $mockResponse = new Response(200, [], json_encode([ + 'success' => true, + 'markdown' => '# Converted Document\n\nThis is the converted content.', + 'tokens' => 30 + ])); + + $mockClient = Mockery::mock(Client::class); + $mockClient->shouldReceive('post') + ->with('/v2/markdown-converter/', [ + 'json' => [ + 'link' => 'https://example.com/document.pdf', + 'resource_type' => 'file' + ] + ]) + ->once() + ->andReturn($mockResponse); + + $wetrocloud = new Wetrocloud('test-api-key'); + $wetrocloudReflection = new ReflectionClass($wetrocloud); + $clientProperty = $wetrocloudReflection->getProperty('client'); + $clientProperty->setAccessible(true); + $clientProperty->setValue($wetrocloud, $mockClient); + + $result = $wetrocloud->markdownConverter('https://example.com/document.pdf', 'file'); + + expect($result)->toBe([ + 'success' => true, + 'markdown' => '# Converted Document\n\nThis is the converted content.', + 'tokens' => 30 + ]); +}); + +test('can convert web page to markdown', function () { + $mockResponse = new Response(200, [], json_encode([ + 'success' => true, + 'markdown' => '# Web Page Title\n\nContent from the web page.', + 'tokens' => 25 + ])); + + $mockClient = Mockery::mock(Client::class); + $mockClient->shouldReceive('post') + ->with('/v2/markdown-converter/', [ + 'json' => [ + 'link' => 'https://example.com/webpage', + 'resource_type' => 'web' + ] + ]) + ->once() + ->andReturn($mockResponse); + + $wetrocloud = new Wetrocloud('test-api-key'); + $wetrocloudReflection = new ReflectionClass($wetrocloud); + $clientProperty = $wetrocloudReflection->getProperty('client'); + $clientProperty->setAccessible(true); + $clientProperty->setValue($wetrocloud, $mockClient); + + $result = $wetrocloud->markdownConverter('https://example.com/webpage', 'web'); + + expect($result)->toBe([ + 'success' => true, + 'markdown' => '# Web Page Title\n\nContent from the web page.', + 'tokens' => 25 + ]); }); +test('can convert image to markdown', function () { + $mockResponse = new Response(200, [], json_encode([ + 'success' => true, + 'markdown' => '![Image Description](image-url)\n\nDescription of the image content.', + 'tokens' => 15 + ])); + + $mockClient = Mockery::mock(Client::class); + $mockClient->shouldReceive('post') + ->with('/v2/markdown-converter/', [ + 'json' => [ + 'link' => 'https://example.com/image.jpg', + 'resource_type' => 'image' + ] + ]) + ->once() + ->andReturn($mockResponse); + + $wetrocloud = new Wetrocloud('test-api-key'); + $wetrocloudReflection = new ReflectionClass($wetrocloud); + $clientProperty = $wetrocloudReflection->getProperty('client'); + $clientProperty->setAccessible(true); + $clientProperty->setValue($wetrocloud, $mockClient); + + $result = $wetrocloud->markdownConverter('https://example.com/image.jpg', 'image'); + + expect($result)->toBe([ + 'success' => true, + 'markdown' => '![Image Description](image-url)\n\nDescription of the image content.', + 'tokens' => 15 + ]); +});