Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrating Cloudflare Workers AI into OpenWebUI #3277

Closed
snakeying opened this issue Jun 18, 2024 · 4 comments
Closed

Integrating Cloudflare Workers AI into OpenWebUI #3277

snakeying opened this issue Jun 18, 2024 · 4 comments

Comments

@snakeying
Copy link

snakeying commented Jun 18, 2024

Is your feature request related to a problem? Please describe.
I have noticed that some people wish to experiment with open source LLMs (such as llama3 or qwen), but are limited by their computer hardware configurations. As a result, they have to purchase services like OpenAI, which can be costly and less accessible.

Describe the solution you'd like
I propose adding support for Cloudflare Workers AI, which offers numerous free models that are sufficient for regular users despite the daily usage limits. This would provide an excellent alternative for those restricted by hardware limitations. Additionally, Cloudflare Workers AI provides free txt2img models. If OpenWebUI could integrate these models into the Image Generation feature, it would greatly enhance the functionality.

Additional context
For more information, please refer to the following URLs:

I hope this feature request can be considered and implemented to benefit more users. Thank you!

@snakeying
Copy link
Author

By converting the Cloudflare Worker AI API to an OpenAI-compatible format, I successfully managed to run all the chat models of Cloudflare Workers AI within OpenWebUI. However, I encountered an issue: I was unable to invoke the text-to-image model through OpenWebUI's Image Generation feature, such as @cf/stabilityai/stable-diffusion-xl-base-1.0.

Project Structure

/test
  |-- Dockerfile
  |-- docker-compose.yml
  |-- index.js
  |-- package.json

Dockerfile

# Use a Node.js base image
FROM node:14

# Set the working directory
WORKDIR /home/test

# Copy package.json and package-lock.json
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the application code
COPY . .

# Expose the port the app runs on
EXPOSE 8080

# Command to run the application
CMD ["node", "index.js"]

index.js

const express = require('express');
const fetch = require('node-fetch');
const app = express();

app.use(express.json());

// Test route for the root path
app.get('/', (req, res) => {
  res.send('Server is running');
});

app.post('/v1/images/generations', async (req, res) => {
  const response = await fetch(`https://api.cloudflare.com/client/v4/accounts/MY_ACCOUNT_ID/ai/run/@cf/stabilityai/stable-diffusion-xl-base-1.0`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.CF_API_TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      prompt: req.body.prompt,
      num_steps: req.body.num_steps || 20,
      strength: req.body.strength || 1,
      guidance: req.body.guidance || 7.5
    })
  });

  if (!response.ok) {
    const errorDetails = await response.json();
    return res.status(response.status).json(errorDetails);
  }

  const imageBuffer = await response.buffer();
  res.set('Content-Type', 'image/png');
  res.send(imageBuffer);
});

app.listen(8080, () => {
  console.log('Server running on port 8080');
});

package.json

{
  "name": "my-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "express": "^4.17.1",
    "node-fetch": "^2.6.1"
  }
}

docker-compose.yml

version: '3'
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - CF_API_TOKEN=my_cloudflare_api_token

Steps to Deploy

docker-compose build
docker-compose up

by testing

curl -X POST http://localhost:8080/v1/images/generations
-H "Content-Type: application/json"
-d '{
"prompt": "A beautiful landscape",
"num_steps": 20,
"strength": 1,
"guidance": 7.5
}' --output generated_image.png

I have successfully generated images, but I'm unable to call them in OpenWebUI, here's the configuration for openwebui

Base URL: http://localhost:8080
API Key: anything
Model Name: @cf/stabilityai/stable-diffusion-xl-base-1.0

Could you please help me identify where the issue might be occurring? I sincerely hope for your assistance.

@justinh-rahb
Copy link
Collaborator

justinh-rahb commented Jun 19, 2024

There should be a much easier/cleaner ways to do this via our Pipelines sister-project... in fact it's basically made for this sort of thing.

@tjbck
Copy link
Contributor

tjbck commented Jun 19, 2024

@snakeying
Copy link
Author

snakeying commented Jun 20, 2024

Based on the information from the Cloudflare Workers AI documentation (https://developers.cloudflare.com/workers-ai/configuration/open-ai-compatibility/), it seems that the current OpenAI-compatible API endpoints for Workers AI only support text generation and text embedding models, specifically through /v1/chat/completions and /v1/embeddings endpoints.

Therefore, I tried this

python code

from typing import List, Union, Generator, Iterator
from pydantic import BaseModel
import os
import requests

class Pipeline:
 class Valves(BaseModel):
     API_BASE_URL: str = "https://api.cloudflare.com/client/v4/accounts/your_account_id/ai/v1"
     API_KEY: str = ""

 def __init__(self):
     self.type = "manifold"
     self.name = "WorkersAI: "

     self.valves = self.Valves(
         **{
             "API_KEY": os.getenv("API_KEY", "your-cloudflare-api-key-here")
         }
     )
     self.pipelines = self.get_models()

 async def on_startup(self):
     print(f"on_startup:{__name__}")

 async def on_shutdown(self):
     print(f"on_shutdown:{__name__}")

 async def on_valves_updated(self):
     print(f"on_valves_updated:{__name__}")
     self.pipelines = self.get_models()

 def get_models(self):
     return [
         {
             "id": "@cf/bytedance/stable-diffusion-xl-lightning",
             "name": "Stable Diffusion XL Lightning"
         }
     ]

 def pipe(
     self, user_message: str, model_id: str, messages: List[dict], body: dict
 ) -> Union[str, Generator, Iterator]:
     print(f"pipe:{__name__}")

     headers = {
         "Authorization": f"Bearer {self.valves.API_KEY}",
         "Content-Type": "application/json"
     }

     payload = {
         "model": model_id,
         "inputs": {
             "prompt": user_message
         }
     }

     try:
         response = requests.post(
             url=f"{self.valves.API_BASE_URL}/run",
             json=payload,
             headers=headers
         )
         response.raise_for_status()

         if response.headers.get('Content-Type') == 'image/png':
             image_url = response.json().get("url")
             yield f"![image]({image_url})\n"
         else:
             yield response.text

     except Exception as e:
         return f"Error: {e}"

However, it doesn't work...

@open-webui open-webui locked and limited conversation to collaborators Jun 29, 2024
@tjbck tjbck converted this issue into discussion #3529 Jun 29, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants