DEV Community

Cover image for Integrating Cloudflare Workers AI with Laravel Livewire 3
Snehal Rajeev Moon
Snehal Rajeev Moon

Posted on • Edited on

Integrating Cloudflare Workers AI with Laravel Livewire 3

Hello Artisans,

Adding AI to your web apps can really boost how well they work and how users like them. A great way to achieve this is by combining Cloudflare Workers AI with Laravel Livewire. This post will guide you on how to set up and use Cloudflare Workers AI in a Laravel Livewire application to make a smart and changing web application.

In this blog post, we will see an example where you can upload an image and convert that image to text using the Cloudflare Workers AI model Image to Text using @cf/llava-hf/llava-1.5-7b-hf, and then analyze the sentiment of that text using another Cloudflare Workers AI model, @cf/huggingface/distilbert-sst-2-int8.

Using these powerful AI models and Livewire's reactive components, we'll build a seamless workflow that transforms and analyzes user-uploaded images in real-time.

What is Cloudflare Workers AI?
Cloudflare Workers is a serverless platform. This gives you fast response times and high performance. Cloudflare has now added AI features to Workers and we will use those Workers AI using REST API.

What is Laravel Livewire?
Laravel Livewire is a framework that works with all parts of Laravel. It helps you make dynamic interfaces easy, while still using Laravel. With Livewire, you can build reactive dynamic interfaces using clear expressive PHP code.

Prerequisites
Before we start, make sure you have the following:

  • A Laravel 11 application set up
  • Composer installed
  • NPM installed
  • Cloudflare account

Step 1: Setting Up Cloudflare Workers

1. Sign in to Cloudflare: If you don't have an account, you must create one.
2. Generate an API Key: Go to the API Tokens section under My Profile. Generate a new API key with appropriate permissions to access Workers AI functionalities. (use a template for Workers AI). or Visit this link Create API Token.
3. Get Account ID: to use Workers AI REST API to make API calls.

Image description

Step 2: Setup env variables

  1. Add this to your .env file.
CLOUDFLARE_API_KEY=
CLOUDFLARE_ACCOUNT_ID=
Enter fullscreen mode Exit fullscreen mode
  1. Create a new config file cloudflare.php to use these env variables.
<?php

return [
    'api_key' => env('CLOUDFLARE_API_KEY'),
    'account_id' => env('CLOUDFLARE_ACCOUNT_ID'),
    'url' => 'https://api.cloudflare.com/client/v4/accounts/'
];
Enter fullscreen mode Exit fullscreen mode

Step 3: Setting Up Laravel Livewire
Next, let's set up a Laravel Livewire component that interacts with our Cloudflare Worker.

1. Install Livewire:

composer require livewire/livewire
Enter fullscreen mode Exit fullscreen mode

2. Publish Livewire Assets:

php artisan livewire:publish
Enter fullscreen mode Exit fullscreen mode
  1. Create a template layout
PHP artisan livewire:layout
Enter fullscreen mode Exit fullscreen mode

4. Create a Livewire Component:

php artisan make:livewire upload-image
Enter fullscreen mode Exit fullscreen mode

It will create a two file one is blade file and another one is class file.

  • Open upload-image.blade.php file and add the below code.
<div class="h-screen p-10">
    <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
    <script src="https://unpkg.com/alpinejs@2.x.x/dist/alpine.min.js" defer></script>
    <div class="border border-pink-700 h-full flex flex-col justify-between relative m-auto w-1/2 m-auto">
        <div class="text-center">
            @if (session()->get('image_url'))
            <div class="mt-6">
                <img src="{{ session()->get('image_url') }}" alt="Uploaded Image"
                    class="w-2/6 h-auto mx-auto rounded-lg shadow-lg">
            </div>
            @endif
            @if (session()->get('text'))
            <h1 class="text-center italic font-bold m-4">Text generated from an image</h1>
            <div class="text-justify px-6 mt-2">
                <p class="py-4">{{ session()->get('text') }}</p>
                <h2 class="italic font-bold">Sentiment analysis of text generated by an image</h2>
                <p class="py-2">
                    <b>{{ session()->get('sentiments')['result'][0]['label'] }}</b> -
                    {{ session()->get('sentiments')['result'][0]['score'] }}
                </p>
                <p class="py-2">
                    <b>{{ session()->get('sentiments')['result'][1]['label'] }}</b> -
                    {{ session()->get('sentiments')['result'][1]['score'] }}
                </p>
            </div>
            @endif
        </div>
        <div class="absolute bottom-0 w-full p-6 bg-gray-50 border-t border-gray-200">
            <form class="space-y-3 text-center" method="post" enctype="multipart/form-data"
                wire:submit.prevent="uploadImage">
                <div x-data="{ selectedFile: '' }">
                    <div
                        class="relative w-full h-10 border border-gray-300 rounded-lg overflow-hidden pl-3 pr-1 flex items-center">
                        <input type="file" name="file" id="file-upload" wire:model="file"
                            class="w-1/2 h-full opacity-0 cursor-pointer absolute top-0 left-0 right-0" accept="image/*"
                            @change="selectedFile = $event.target.files[0]? $event.target.files[0].name : ''; $wire.deleteExistingImage()">
                        <label for="file-upload"
                            class="text-gray-500 font-medium truncate w-full h-full flex items-center">
                            <span x-text="selectedFile || 'No file selected'"></span>
                        </label>
                        <button type="submit"
                            class="bg-blue-500 text-white font-bold rounded-lg px-3 py-1 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-400">
                            Upload
                        </button>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>
<script>
    window.onload = function() {
        @php
            if (session()->get('image_url')) {
                $imagePath = storage_path('app/public/' . basename(session()->get('image_url')));
                if (file_exists($imagePath)) {
                    unlink($imagePath);
                }
                session()->forget('image_url');
            }
       @endphp
        };
</script>

Enter fullscreen mode Exit fullscreen mode
  • Open a class file UploadImage.php
<?php

namespace App\Livewire;

use Livewire\Component;
use Livewire\WithFileUploads;
use Illuminate\Support\Facades\Http;

class UploadImage extends Component
{
    use WithFileUploads;

    public $file = "";
    public $image_url;

    public function render()
    {
        return view('livewire.upload-image');
    }

    public function uploadImage()
    {
        $this->validate([
            'file' => 'image|max:1024', // 1MB Max
        ]);

        $image = $this->file;
        $imagePath = $image->store('images', 'public');
        $this->image_url = '/storage/' . $imagePath;

        // We are using session to store this data
        session()->put('image_url', $this->image_url);

        $info = $this->createTextFromImg($this->file);
        $sentiments = $this->sentimentAnalysis($info['result']['description']);

        return redirect()->back()->with(['text' => $info['result']['description'], 'sentiments' => $sentiments]);
    }

    public function sentimentAnalysis($text)
    {
        try {

            $authorizationToken = config('cloudflare.api_key');
            $accountId = config('cloudflare.account_id');

            $url = 'https://api.cloudflare.com/client/v4/accounts/' . $accountId . '/ai/run/@cf/huggingface/distilbert-sst-2-int8';

            $response = Http::withToken(
                $authorizationToken
            )
                ->post($url, [
                    'text' => $text
                ]);

            return json_decode($response->getBody(), true);
        } catch (\Exception $e) {
            return response()->json(['error' => $e->getMessage()], 500);
        }
    }

    public function createTextFromImg($img)
    {
        try {
            if (!empty($img)) {

                $imageData = file_get_contents($img->getRealPath());
                $imageArray = unpack('C*', $imageData);

                // Prepare input for the AI service
                $input = [
                    'image' => array_values($imageArray),
                    'prompt' => 'Generate a caption for this image by extracting all the details.',
                    'max_tokens' => 512,
                ];

                $authorizationToken = config('cloudflare.api_key');
                $accountId = config('cloudflare.account_id');
                $baseURL = config('cloudflare.url');
                $url = $baseURL . $accountId . '/ai/run/@cf/llava-hf/llava-1.5-7b-hf';

                $response = Http::withToken(
                    $authorizationToken
                )
                    ->post($url, $input);

                if ($response->successful()) {
                    return $response->json();
                } else {
                    return ['error' => 'Failed to get response from AI service'];
                }
            }
        } catch (\Exception $e) {
            return response()->json(['error' => $e->getMessage()], 500);
        }
    }

    public function deleteExistingImage()
    {
        if (session()->get('image_url')) {
            $imagePath = storage_path('app/public/' . basename(session()->get('image_url')));
            if (file_exists($imagePath)) {
                unlink($imagePath);
            }
            session()->forget('image_url');
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
  • WithFileUploads trait helps developers manage file uploads in Livewire components. This trait makes it easier to upload files by taking care of file storage and checking. It allows developers to handle file uploads in real-time without refreshing the page.

Output:

Image description

Image description

Here is the link for Github.

Documentation

Add env variables

CLOUDFLARE_API_KEY=
CLOUDFLARE_ACCOUNT_ID=
Enter fullscreen mode Exit fullscreen mode

Install Livewire:

composer require livewire/livewire
Enter fullscreen mode Exit fullscreen mode

Publish Livewire Assets:

php artisan livewire:publish
Enter fullscreen mode Exit fullscreen mode

Create a template layout

PHP artisan livewire:layout
Enter fullscreen mode Exit fullscreen mode

Create a Livewire Component:

php artisan make:livewire upload-image
Enter fullscreen mode Exit fullscreen mode





Conclusion
In summary, combining Cloudflare Workers AI with Laravel Livewire allows us to enhance our web applications smarter and interactive with trends in AI stuff.
Using models like @cf/llava-hf/llava-1.5-7b-hf to turn images into text and @cf/huggingface/distilbert-sst-2-int8 to figure out how people feel by analyzing their sentiments, developers can build seamless application. It's a good place to start if you want to create new apps that people find easy to use. Check out this strong combo and see what cool stuff you can make next!

Happy Coding!!
🦄 ❤️

Top comments (1)

Collapse
 
naveen_dev profile image
Naveen Kola • Edited

Very useful information. Thanks for writing 🙌