├── README.md ├── docs ├── README.de.md ├── README.es.md ├── README.fr.md ├── README.ja.md ├── README.ru.md ├── README.zh-cn.md └── README.zh-hant.md └── src ├── flux-api-worker_de.js ├── flux-api-worker_en.js ├── flux-api-worker_es.js ├── flux-api-worker_fr.js ├── flux-api-worker_ja.js ├── flux-api-worker_ru.js ├── flux-api-worker_zh-cn.js └── flux-api-worker_zh-hant /README.md: -------------------------------------------------------------------------------- 1 | # Flux-API-Worker - README 📘🎨🤖 2 | 3 | [English](./README.md) | [简体中文](./docs/README.zh-cn.md) | [繁體中文](./docs/README.zh-hant.md) | [日本語](./docs/README.ja.md) | [Español](./docs/README.es.md) | [Français](./docs/README.fr.md) | [Русский](./docs/README.ru.md) | [Deutsch](./docs/README.de.md) 4 | 5 | ## Introduction 🌟💡 6 | 7 | Flux-API-Worker is an AI image generation service deployed on Cloudflare Workers. It utilizes Cloudflare's Flux model to generate images and provides an efficient API interface for handling requests. This service can be easily integrated into various applications, offering users powerful AI image generation capabilities. ✨🖼️🚀 8 | 9 | ## Features 🚀🌈 10 | 11 | - 🎨 Support for custom prompts to generate images 12 | - 🌐 Optional prompt optimization feature 13 | - 📐 Support for multiple preset image sizes and aspect ratios 14 | - 💾 Use of Cloudflare KV to store generated images 15 | - 🔄 Support for streaming and non-streaming responses 16 | - 🔒 Built-in system messages to ensure consistent output quality 17 | - 🌍 Cross-Origin Resource Sharing (CORS) support 18 | 19 | ## Quick Start 🏃‍♂️💨 20 | 21 | ### Deploy in Cloudflare Dashboard 🖥️🛠️ 22 | 23 | 1. Log in to your Cloudflare account and navigate to the Workers page. 👨‍💻👩‍💻 24 | 2. Click the "Create a Service" button. 🆕 25 | 3. Name your Worker, e.g., "flux-api". ✏️ 26 | 4. Paste the provided Worker code into the editor. 📋 27 | 5. Click the "Save and Deploy" button. 🚀 28 | 29 | ### Set Up Environment Variables ⚙️🔧 30 | 31 | In the Worker's settings page, find the "Environment Variables" section and add the following variables: 32 | 33 | ## Environment Variables List 📋🔑 34 | 35 | | Variable Name | Description | Type | Example | Default | 36 | |---------------|-------------|------|---------|---------| 37 | | `API_KEY` | API authentication key 🔐 | String | `"your-complex-api-key-here"` | - | 38 | | `CF_ACCOUNT_ID` | Cloudflare Account ID 🆔 | String | `"1a2b3c4d5e6f7g8h9i0j"` | - | 39 | | `CF_API_TOKEN` | Cloudflare API Token 🎟️ | String | `"your-cloudflare-api-token"` | - | 40 | | `PROMPT_OPTIMIZATION` | Enable Prompt Optimization 🌐 | String | `"true"` or `"false"` | - | 41 | | `EXTERNAL_API_BASE` | External API base URL 🔗 | String | `"https://api.external-service.com"` | - | 42 | | `EXTERNAL_MODEL` | External optimization model name 🤖 | String | `"gpt-3.5-turbo"` | - | 43 | | `EXTERNAL_API_KEY` | External API access key 🗝️ | String | `"your-external-api-key"` | - | 44 | | `FLUX_NUM_STEPS` | Number of steps for Flux model 🚶 | Integer | `"4"` | 4 | 45 | | `IMAGE_EXPIRATION` | Image expiration time in KV (seconds) ⏳ | Integer | `"1800"` | 1800 | 46 | 47 | Ensure these variables are correctly configured in your Cloudflare Worker's environment variables settings. For variables with default values, you can keep the default if no change is needed. 🔧✅ 48 | 49 | > Note: For security, set a complex string for `API_KEY`. This will be used to validate the legitimacy of API calls. 🔒🛡️ 50 | 51 | ### Create KV Namespace 🗄️📦 52 | 53 | 1. In the Cloudflare Dashboard, go to the "Workers" page. 🖥️ 54 | 2. Click on the "KV" tab. 📑 55 | 3. Create a new namespace named "FLUX_CF_KV". 🆕 56 | 4. In the Worker's settings, bind this KV namespace to the `FLUX_CF_KV` variable. 🔗 57 | 58 | ## API Endpoints and Functionality 🌐🛠️ 59 | 60 | ### 1. Welcome Page 👋 61 | 62 | Accessing the Worker's root path (`https://..workers.dev/`) will display a welcome page confirming the API service is running. ✅🏠 63 | 64 | ### 2. Chat Completions Endpoint 💬 65 | 66 | Main image generation endpoint: 67 | ``` 68 | https://..workers.dev/v1/chat/completions 69 | ``` 70 | 🎨✨ 71 | 72 | ### 3. Model Information Endpoint ℹ️ 73 | 74 | Get available model information: 75 | ``` 76 | https://..workers.dev/v1/models 77 | ``` 78 | This endpoint returns information about the currently used Flux model. 🤖📊 79 | 80 | ### 4. Image Retrieval Endpoint 🖼️ 81 | 82 | Retrieve generated images: 83 | ``` 84 | https://..workers.dev/image/{image_key} 85 | ``` 86 | 📥🎭 87 | 88 | ## Usage Guide 📖🧭 89 | 90 | ### Generate Images 🖼️🎨 91 | 92 | Send a POST request to the chat completions endpoint in the following format: 93 | 94 | ```json 95 | { 96 | "messages": [ 97 | { 98 | "role": "user", 99 | "content": "A cute cat 3:2" 100 | } 101 | ], 102 | "stream": false 103 | } 104 | ``` 105 | 106 | The request headers must include: 107 | 108 | ``` 109 | Authorization: Bearer YOUR_API_KEY 110 | Content-Type: application/json 111 | ``` 112 | 113 | > Important: Replace `YOUR_API_KEY` with the `API_KEY` value you set in the environment variables. 🔑🔄 114 | 115 | ### Streaming Response 🌊📡 116 | 117 | If you want to receive a streaming response, set the `stream` parameter to `true`: 118 | 119 | ```json 120 | { 121 | "messages": [ 122 | { 123 | "role": "user", 124 | "content": "A cute cat 3:2" 125 | } 126 | ], 127 | "stream": true 128 | } 129 | ``` 130 | 131 | Streaming responses will be returned in Server-Sent Events (SSE) format, allowing real-time generation progress updates. ⚡🔄 132 | 133 | ### Supported Image Sizes 📏🖼️ 134 | 135 | Flux-API-Worker supports the following preset image sizes and aspect ratios: 136 | 137 | - 1:1 (1024x1024) - Default size 🟦 138 | - 1:2 (512x1024) 📱 139 | - 3:2 (768x512) 🖼️ 140 | - 3:4 (768x1024) 📱 141 | - 16:9 (1024x576) 🖥️ 142 | - 9:16 (576x1024) 📱 143 | 144 | To specify a particular size, simply add the corresponding ratio after the prompt, for example: 145 | 146 | ``` 147 | "A cute cat 16:9" 148 | ``` 149 | 150 | If no size is specified, the system will default to generating a 1:1 (1024x1024) image. 🎛️🔧 151 | 152 | ### Cross-Origin Resource Sharing (CORS) Support 🌍🔓 153 | 154 | Flux-API-Worker supports CORS, allowing access to the API from web applications on different domains. This means you can call the API directly from frontend JavaScript applications without encountering cross-origin issues. 🔗🚫🚧 155 | 156 | ### Using in Third-Party Applications 🔗🔌 157 | 158 | Flux-API-Worker can be easily integrated into various applications such as NextWeb, ChatBox, etc. When configuring in these applications: 159 | 160 | 1. Set the API address to your Worker URL (chat completions endpoint). 🔗 161 | 2. Enter the API KEY you set. 🔑 162 | 3. No need to consider the System Message settings provided by the application, as the Flux-API-Worker uses a built-in System Message. 💬🚫 163 | 164 | > Note: Flux-API-Worker has removed the context functionality, generating a new unique image with each call. 🆕🖼️ 165 | 166 | ### Response Format 📤📊 167 | 168 | Example of a non-streaming response: 169 | 170 | ```json 171 | { 172 | "id": "chatcmpl-1234567890", 173 | "created": 1677649420, 174 | "model": "@cf/black-forest-labs/flux-1-schnell", 175 | "object": "chat.completion", 176 | "choices": [ 177 | { 178 | "index": 0, 179 | "message": { 180 | "role": "assistant", 181 | "content": "🎨 Original prompt: A cute cat 3:2\n💬 Prompt generation model: Original Prompt\n🌐 Optimized prompt: A cute cat\n📐 Image specifications: 768x512\n🌟 Image generation successful!\nHere's the result:\n\n![Generated Image](https://your-worker-url.workers.dev/image/12345)" 182 | }, 183 | "finish_reason": "stop" 184 | } 185 | ], 186 | "usage": { 187 | "prompt_tokens": 20, 188 | "completion_tokens": 100, 189 | "total_tokens": 120 190 | } 191 | } 192 | ``` 193 | 194 | ## Considerations ⚠️🚨 195 | 196 | - Ensure all necessary environment variables are correctly set. ✅🔧 197 | - API keys should be kept secure and not exposed in client-side code. 🔒🙈 198 | - Images in KV storage have an expiration time (default 30 minutes), so save important images promptly. ⏳💾 199 | - If the prompt optimization feature is enabled, please ensure the external API is configured correctly. 🌐🔧 200 | - When using streaming responses, make sure your client can properly handle Server-Sent Events. 🌊📡 201 | 202 | ## Troubleshooting 🔧🚑 203 | 204 | 1. For unauthorized errors, check if the API key is correctly set and used. 🔑❓ 205 | 2. If image generation fails, verify that the Cloudflare API Token has the correct permissions. 🎟️🔍 206 | 3. If prompt optimization is not working, please ensure `PROMPT_OPTIMIZATION` is set to 'true' and the external API is properly configured. 🌐🔧 207 | 4. For 404 errors, ensure you're accessing the correct endpoint path. 🔍🚷 208 | 5. For other errors, check the Worker's logs for more detailed error information. 📋🔬 209 | 210 | ## Further Customization 🛠️🎨 211 | 212 | You can further optimize the API's functionality by modifying the Worker code, for example: 213 | 214 | - Adjust supported image sizes and aspect ratios 📏✂️ 215 | - Modify built-in system messages to change prompt generation behavior 💬🔄 216 | - Add additional error handling and logging mechanisms 🚨📊 217 | - Implement custom rate limiting or other security measures 🛡️⏱️ 218 | 219 | I hope this README helps you quickly deploy and use Flux-API-Worker. If you have any questions or need further assistance, please feel free to contact me. 💌👨‍💻👩‍💻 220 | 221 | If you find this repo helpful, please give it a star. ⭐⭐⭐ Thank you! 222 | -------------------------------------------------------------------------------- /docs/README.de.md: -------------------------------------------------------------------------------- 1 | # Flux-API-Worker - README 📘🎨🤖 2 | 3 | [English](../README.md) | [简体中文](./README.zh-cn.md) | [繁體中文](./README.zh-hant.md) | [日本語](./README.ja.md) | [Español](./README.es.md) | [Français](./README.fr.md) | [Русский](./README.ru.md) | [Deutsch](./README.de.md) 4 | 5 | ## Einführung 🌟💡 6 | 7 | Flux-API-Worker ist ein KI-Bildgenerierungsdienst, der auf Cloudflare Workers bereitgestellt wird. Er nutzt das von Cloudflare bereitgestellte Flux-Modell zur Bilderzeugung und bietet eine effiziente API-Schnittstelle zur Verarbeitung von Anfragen. Dieser Dienst lässt sich einfach in verschiedene Anwendungen integrieren und bietet Benutzern leistungsstarke KI-Bildgenerierungsfähigkeiten. ✨🖼️🚀 8 | 9 | ## Funktionen 🚀🌈 10 | 11 | - 🎨 Unterstützung benutzerdefinierter Prompts zur Bilderzeugung 12 | - 🌐 Optionale Prompt-Optimierungsfunktion 13 | - 📐 Unterstützung verschiedener voreingestellter Bildgrößen und Seitenverhältnisse 14 | - 💾 Speicherung generierter Bilder mit Cloudflare KV 15 | - 🔄 Unterstützung für Stream- und Nicht-Stream-Antworten 16 | - 🔒 Integrierte Systemnachrichten für konsistente Ausgabequalität 17 | - 🌍 Cross-Origin Resource Sharing (CORS) Unterstützung 18 | 19 | ## Schnellstart 🏃‍♂️💨 20 | 21 | ### Bereitstellung im Cloudflare Dashboard 🖥️🛠️ 22 | 23 | 1. Melden Sie sich bei Ihrem Cloudflare-Konto an und gehen Sie zur Workers-Seite. 👨‍💻👩‍💻 24 | 2. Klicken Sie auf die Schaltfläche "Dienst erstellen". 🆕 25 | 3. Benennen Sie Ihren Worker, z.B. "flux-api". ✏️ 26 | 4. Fügen Sie den bereitgestellten Worker-Code in den Editor ein. 📋 27 | 5. Klicken Sie auf die Schaltfläche "Speichern und bereitstellen". 🚀 28 | 29 | ### Umgebungsvariablen einrichten ⚙️🔧 30 | 31 | Finden Sie im Einstellungsbereich Ihres Workers den Abschnitt "Umgebungsvariablen" und fügen Sie die folgenden Variablen hinzu: 32 | 33 | ## Liste der Umgebungsvariablen 📋🔑 34 | 35 | | Variablenname | Beschreibung | Typ | Beispiel | Standardwert | 36 | |---------------|--------------|-----|----------|--------------| 37 | | `API_KEY` | API-Authentifizierungsschlüssel 🔐 | Zeichenkette | `"your-complex-api-key-here"` | - | 38 | | `CF_ACCOUNT_ID` | Cloudflare-Konto-ID 🆔 | Zeichenkette | `"1a2b3c4d5e6f7g8h9i0j"` | - | 39 | | `CF_API_TOKEN` | Cloudflare API-Token 🎟️ | Zeichenkette | `"your-cloudflare-api-token"` | - | 40 | | `PROMPT_OPTIMIZATION` | Prompt-Optimierung aktivieren 🌐 | Zeichenkette | `"true"` oder `"false"` | - | 41 | | `EXTERNAL_API_BASE` | Basis-URL der externen API 🔗 | Zeichenkette | `"https://api.external-service.com"` | - | 42 | | `EXTERNAL_MODEL` | Externer Optimierungsmodellname 🤖 | Zeichenkette | `"gpt-3.5-turbo"` | - | 43 | | `EXTERNAL_API_KEY` | Zugriffsschlüssel für externe API 🗝️ | Zeichenkette | `"your-external-api-key"` | - | 44 | | `FLUX_NUM_STEPS` | Anzahl der Schritte für das Flux-Modell 🚶 | Ganzzahl | `"4"` | 4 | 45 | | `IMAGE_EXPIRATION` | Ablaufzeit der Bilder im KV (in Sekunden) ⏳ | Ganzzahl | `"1800"` | 1800 | 46 | 47 | Stellen Sie sicher, dass Sie diese Variablen in den Umgebungsvariablen-Einstellungen Ihres Cloudflare Workers korrekt konfigurieren. Für Variablen mit Standardwerten können Sie die Standardeinstellungen beibehalten, wenn keine Änderungen erforderlich sind. 🔧✅ 48 | 49 | > Hinweis: Aus Sicherheitsgründen sollten Sie für `API_KEY` eine komplexe Zeichenfolge festlegen. Diese wird zur Überprüfung der Gültigkeit von API-Aufrufen verwendet. 🔒🛡️ 50 | 51 | ### KV-Namespace erstellen 🗄️📦 52 | 53 | 1. Gehen Sie im Cloudflare Dashboard zur Seite "Workers". 🖥️ 54 | 2. Klicken Sie auf die Registerkarte "KV". 📑 55 | 3. Erstellen Sie einen neuen Namespace mit dem Namen "FLUX_CF_KV". 🆕 56 | 4. Binden Sie in den Worker-Einstellungen diesen KV-Namespace an die Variable `FLUX_CF_KV`. 🔗 57 | 58 | ## API-Endpunkte und Funktionen 🌐🛠️ 59 | 60 | ### 1. Willkommensseite 👋 61 | 62 | Der Zugriff auf den Root-Pfad des Workers (`https://..workers.dev/`) zeigt eine Willkommensseite an, die bestätigt, dass der API-Dienst läuft. ✅🏠 63 | 64 | ### 2. Chat-Completion-Endpunkt 💬 65 | 66 | Hauptendpunkt für die Bilderzeugung: 67 | ``` 68 | https://..workers.dev/v1/chat/completions 69 | ``` 70 | 🎨✨ 71 | 72 | ### 3. Modellinformations-Endpunkt ℹ️ 73 | 74 | Abrufen von Informationen zu verfügbaren Modellen: 75 | ``` 76 | https://..workers.dev/v1/models 77 | ``` 78 | Dieser Endpunkt gibt Informationen über das aktuell verwendete Flux-Modell zurück. 🤖📊 79 | 80 | ### 4. Bildabruf-Endpunkt 🖼️ 81 | 82 | Abrufen generierter Bilder: 83 | ``` 84 | https://..workers.dev/image/{image_key} 85 | ``` 86 | 📥🎭 87 | 88 | ## Bedienungsanleitung 📖🧭 89 | 90 | ### Bild generieren 🖼️🎨 91 | 92 | Senden Sie eine POST-Anfrage an den Chat-Completion-Endpunkt im folgenden Format: 93 | 94 | ```json 95 | { 96 | "messages": [ 97 | { 98 | "role": "user", 99 | "content": "Eine niedliche Katze 3:2" 100 | } 101 | ], 102 | "stream": false 103 | } 104 | ``` 105 | 106 | Der Anfrage-Header muss Folgendes enthalten: 107 | 108 | ``` 109 | Authorization: Bearer YOUR_API_KEY 110 | Content-Type: application/json 111 | ``` 112 | 113 | > Wichtig: Ersetzen Sie `YOUR_API_KEY` durch den Wert, den Sie in den Umgebungsvariablen für `API_KEY` festgelegt haben. 🔑🔄 114 | 115 | ### Stream-Antwort 🌊📡 116 | 117 | Wenn Sie eine Stream-Antwort erhalten möchten, setzen Sie den `stream`-Parameter auf `true`: 118 | 119 | ```json 120 | { 121 | "messages": [ 122 | { 123 | "role": "user", 124 | "content": "Eine niedliche Katze 3:2" 125 | } 126 | ], 127 | "stream": true 128 | } 129 | ``` 130 | 131 | Stream-Antworten werden im Server-Sent Events (SSE) Format zurückgegeben, wodurch ein Echtzeit-Fortschritt der Generierung möglich ist. ⚡🔄 132 | 133 | ### Unterstützte Bildgrößen 📏🖼️ 134 | 135 | Flux-API-Worker unterstützt die folgenden voreingestellten Bildgrößen und Seitenverhältnisse: 136 | 137 | - 1:1 (1024x1024) - Standardgröße 🟦 138 | - 1:2 (512x1024) 📱 139 | - 3:2 (768x512) 🖼️ 140 | - 3:4 (768x1024) 📱 141 | - 16:9 (1024x576) 🖥️ 142 | - 9:16 (576x1024) 📱 143 | 144 | Um eine bestimmte Größe anzugeben, fügen Sie einfach das entsprechende Verhältnis am Ende des Prompts hinzu, zum Beispiel: 145 | 146 | ``` 147 | "Eine niedliche Katze 16:9" 148 | ``` 149 | 150 | Wenn keine Größe angegeben wird, generiert das System standardmäßig ein 1:1 (1024x1024) Bild. 🎛️🔧 151 | 152 | ### Cross-Origin Resource Sharing (CORS) Unterstützung 🌍🔓 153 | 154 | Flux-API-Worker unterstützt CORS, wodurch der Zugriff auf die API von Webanwendungen aus verschiedenen Domains ermöglicht wird. Das bedeutet, Sie können die API direkt in Frontend-JavaScript-Anwendungen aufrufen, ohne auf Probleme mit Cross-Origin-Anfragen zu stoßen. 🔗🚫🚧 155 | 156 | ### Verwendung in Drittanbieter-Anwendungen 🔗🔌 157 | 158 | Flux-API-Worker lässt sich einfach in verschiedene Anwendungen wie NextWeb, ChatBox usw. integrieren. Bei der Konfiguration in diesen Anwendungen: 159 | 160 | 1. Setzen Sie die API-Adresse auf Ihre Worker-URL (Chat-Completion-Endpunkt). 🔗 161 | 2. Geben Sie den von Ihnen festgelegten API-KEY ein. 🔑 162 | 3. Es ist nicht erforderlich, die vom System bereitgestellten System Message-Einstellungen zu beachten, da Flux-API-Worker eine integrierte System Message verwendet. 💬🚫 163 | 164 | > Hinweis: Flux-API-Worker hat die Kontextfunktion entfernt. Jeder Aufruf generiert ein neues, einzigartiges Bild. 🆕🖼️ 165 | 166 | ### Antwortformat 📤📊 167 | 168 | Beispiel einer Nicht-Stream-Antwort: 169 | 170 | ```json 171 | { 172 | "id": "chatcmpl-1234567890", 173 | "created": 1677649420, 174 | "model": "@cf/black-forest-labs/flux-1-schnell", 175 | "object": "chat.completion", 176 | "choices": [ 177 | { 178 | "index": 0, 179 | "message": { 180 | "role": "assistant", 181 | "content": "🎨 Originaler Prompt: Eine niedliche Katze 3:2\n💬 Prompt-Generierungsmodell: Original Prompt\n🌐 Optimiertes Prompt: Eine niedliche Katze\n📐 Bildspezifikation: 768x512\n🌟 Bild erfolgreich generiert!\nHier ist das Ergebnis:\n\n![Generiertes Bild](https://your-worker-url.workers.dev/image/12345)" 182 | }, 183 | "finish_reason": "stop" 184 | } 185 | ], 186 | "usage": { 187 | "prompt_tokens": 20, 188 | "completion_tokens": 100, 189 | "total_tokens": 120 190 | } 191 | } 192 | ``` 193 | 194 | ## Wichtige Hinweise ⚠️🚨 195 | 196 | - Stellen Sie sicher, dass alle erforderlichen Umgebungsvariablen korrekt eingerichtet sind. ✅🔧 197 | - Der API-Schlüssel sollte sicher aufbewahrt und nicht im Client-Code offengelegt werden. 🔒🙈 198 | - Bilder haben eine Ablaufzeit im KV-Speicher (standardmäßig 30 Minuten). Speichern Sie wichtige Bilder rechtzeitig. ⏳💾 199 | - Wenn die Prompt-Optimierungsfunktion aktiviert ist, stellen Sie sicher, dass die externe API korrekt konfiguriert ist. 🌐🔧 200 | - Bei Verwendung von Stream-Antworten stellen Sie sicher, dass Ihr Client Server-Sent Events korrekt verarbeiten kann. 🌊📡 201 | 202 | ## Fehlerbehebung 🔧🚑 203 | 204 | 1. Bei Nicht-Autorisierungsfehlern überprüfen Sie, ob der API-Schlüssel korrekt eingerichtet und verwendet wird. 🔑❓ 205 | 2. Wenn die Bilderzeugung fehlschlägt, überprüfen Sie, ob das Cloudflare API-Token die richtigen Berechtigungen hat. 🎟️🔍 206 | 3. Wenn die Prompt-Optimierung nicht funktioniert, überprüfen Sie, ob `PROMPT_OPTIMIZATION` auf 'true' gesetzt ist und die externe API korrekt konfiguriert ist. 🌐🔧 207 | 4. Bei 404-Fehlern stellen Sie sicher, dass Sie den richtigen Endpunkt-Pfad aufrufen. 🔍🚷 208 | 5. Für andere Fehler überprüfen Sie die Worker-Logs für detailliertere Fehlermeldungen. 📋🔬 209 | 210 | ## Weitere Anpassungen 🛠️🎨 211 | 212 | Sie können den Worker-Code modifizieren, um die API-Funktionalität weiter zu optimieren, zum Beispiel: 213 | 214 | - Anpassen der unterstützten Bildgrößen und Seitenverhältnisse 📏✂️ 215 | - Ändern der integrierten Systemnachrichten, um das Verhalten der Prompt-Generierung zu beeinflussen 💬🔄 216 | - Hinzufügen zusätzlicher Fehlerbehandlungs- und Protokollierungsmechanismen 🚨📊 217 | - Implementieren benutzerdefinierter Ratenbegrenzungen oder anderer Sicherheitsmaßnahmen 🛡️⏱️ 218 | 219 | Ich hoffe, dieses README hilft Ihnen bei der schnellen Bereitstellung und Nutzung von Flux-API-Worker. Wenn Sie Fragen haben oder weitere Hilfe benötigen, zögern Sie nicht, mich zu kontaktieren. 💌👨‍💻👩‍💻 220 | 221 | Wenn Sie finden, dass dieses Repo Ihnen geholfen hat, geben Sie mir bitte einen Stern. ⭐⭐⭐ Vielen Dank! 222 | -------------------------------------------------------------------------------- /docs/README.es.md: -------------------------------------------------------------------------------- 1 | # Flux-API-Worker - README 📘🎨🤖 2 | 3 | [English](../README.md) | [简体中文](./README.zh-cn.md) | [繁體中文](./README.zh-hant.md) | [日本語](./README.ja.md) | [Español](./README.es.md) | [Français](./README.fr.md) | [Русский](./README.ru.md) | [Deutsch](./README.de.md) 4 | ## Introducción 🌟💡 5 | 6 | Flux-API-Worker es un servicio de generación de imágenes con IA desplegado en Cloudflare Workers. Utiliza el modelo Flux proporcionado por Cloudflare para generar imágenes y ofrece una API eficiente para procesar solicitudes. Este servicio se puede integrar fácilmente en diversas aplicaciones, proporcionando a los usuarios potentes capacidades de generación de imágenes con IA. ✨🖼️🚀 7 | 8 | ## Características 🚀🌈 9 | 10 | - 🎨 Soporte para generación de imágenes con prompts personalizados 11 | - 🌐 Función opcional de optimización de prompts 12 | - 📐 Soporte para múltiples tamaños y relaciones de aspecto predefinidas 13 | - 💾 Almacenamiento de imágenes generadas en Cloudflare KV 14 | - 🔄 Soporte para respuestas en streaming y no streaming 15 | - 🔒 Mensajes del sistema incorporados para garantizar una calidad de salida consistente 16 | - 🌍 Soporte para Compartir Recursos de Origen Cruzado (CORS) 17 | 18 | ## Inicio rápido 🏃‍♂️💨 19 | 20 | ### Despliegue en el Panel de Cloudflare 🖥️🛠️ 21 | 22 | 1. Inicie sesión en su cuenta de Cloudflare y vaya a la página de Workers. 👨‍💻👩‍💻 23 | 2. Haga clic en el botón "Crear servicio". 🆕 24 | 3. Asigne un nombre a su Worker, por ejemplo, "flux-api". ✏️ 25 | 4. En el editor, pegue el código del Worker proporcionado. 📋 26 | 5. Haga clic en el botón "Guardar y desplegar". 🚀 27 | 28 | ### Configuración de variables de entorno ⚙️🔧 29 | 30 | En la página de configuración del Worker, busque la sección "Variables de entorno" y añada las siguientes variables: 31 | 32 | ## Lista de variables de entorno 📋🔑 33 | 34 | | Nombre de la variable | Descripción | Tipo | Ejemplo | Valor predeterminado | 35 | |--------|------|------|------|--------| 36 | | `API_KEY` | Clave de autenticación de la API 🔐 | Cadena | `"su-clave-api-compleja-aquí"` | - | 37 | | `CF_ACCOUNT_ID` | ID de cuenta de Cloudflare 🆔 | Cadena | `"1a2b3c4d5e6f7g8h9i0j"` | - | 38 | | `CF_API_TOKEN` | Token de API de Cloudflare 🎟️ | Cadena | `"su-token-api-de-cloudflare"` | - | 39 | | `PROMPT_OPTIMIZATION` | Habilitar Optimización de Prompt 🌐 | Cadena | `"true"` o `"false"` | - | 40 | | `EXTERNAL_API_BASE` | URL base de la API externa 🔗 | Cadena | `"https://api.servicio-externo.com"` | - | 41 | | `EXTERNAL_MODEL` | Nombre del modelo de optimización externo 🤖 | Cadena | `"gpt-3.5-turbo"` | - | 42 | | `EXTERNAL_API_KEY` | Clave de acceso para la API externa 🗝️ | Cadena | `"su-clave-api-externa"` | - | 43 | | `FLUX_NUM_STEPS` | Número de pasos para el modelo Flux 🚶 | Entero | `"4"` | 4 | 44 | | `IMAGE_EXPIRATION` | Tiempo de expiración de las imágenes en KV (segundos) ⏳ | Entero | `"1800"` | 1800 | 45 | 46 | Asegúrese de configurar correctamente estas variables en la configuración de variables de entorno de Cloudflare Worker. Para las variables con valores predeterminados, puede mantener la configuración predeterminada si no necesita cambiarla. 🔧✅ 47 | 48 | > Nota: Por seguridad, establezca una cadena compleja para `API_KEY`. Esto se utilizará para verificar la legitimidad de las llamadas a la API. 🔒🛡️ 49 | 50 | ### Crear un espacio de nombres KV 🗄️📦 51 | 52 | 1. En el Panel de Cloudflare, vaya a la página "Workers". 🖥️ 53 | 2. Haga clic en la pestaña "KV". 📑 54 | 3. Cree un nuevo espacio de nombres llamado "FLUX_CF_KV". 🆕 55 | 4. En la configuración del Worker, vincule este espacio de nombres KV a la variable `FLUX_CF_KV`. 🔗 56 | 57 | ## Endpoints de la API y funcionalidades 🌐🛠️ 58 | 59 | ### 1. Página de bienvenida 👋 60 | 61 | Acceder a la ruta raíz del Worker (`https://..workers.dev/`) mostrará una página de bienvenida, confirmando que el servicio API está en funcionamiento. ✅🏠 62 | 63 | ### 2. Endpoint de completado de chat 💬 64 | 65 | Endpoint principal para la generación de imágenes: 66 | ``` 67 | https://..workers.dev/v1/chat/completions 68 | ``` 69 | 🎨✨ 70 | 71 | ### 3. Endpoint de información del modelo ℹ️ 72 | 73 | Obtener información sobre los modelos disponibles: 74 | ``` 75 | https://..workers.dev/v1/models 76 | ``` 77 | Este endpoint devuelve información sobre el modelo Flux actualmente en uso. 🤖📊 78 | 79 | ### 4. Endpoint de obtención de imágenes 🖼️ 80 | 81 | Obtener imágenes generadas: 82 | ``` 83 | https://..workers.dev/image/{clave_de_imagen} 84 | ``` 85 | 📥🎭 86 | 87 | ## Guía de uso 📖🧭 88 | 89 | ### Generar una imagen 🖼️🎨 90 | 91 | Envíe una solicitud POST al endpoint de completado de chat con el siguiente formato: 92 | 93 | ```json 94 | { 95 | "messages": [ 96 | { 97 | "role": "user", 98 | "content": "Un gato adorable 3:2" 99 | } 100 | ], 101 | "stream": false 102 | } 103 | ``` 104 | 105 | Los encabezados de la solicitud deben incluir: 106 | 107 | ``` 108 | Authorization: Bearer SU_CLAVE_API 109 | Content-Type: application/json 110 | ``` 111 | 112 | > Importante: Reemplace `SU_CLAVE_API` con el valor de `API_KEY` que estableció en las variables de entorno. 🔑🔄 113 | 114 | ### Respuesta en streaming 🌊📡 115 | 116 | Si desea recibir una respuesta en streaming, establezca el parámetro `stream` en `true`: 117 | 118 | ```json 119 | { 120 | "messages": [ 121 | { 122 | "role": "user", 123 | "content": "Un gato adorable 3:2" 124 | } 125 | ], 126 | "stream": true 127 | } 128 | ``` 129 | 130 | Las respuestas en streaming se devolverán en formato Server-Sent Events (SSE), permitiendo obtener el progreso de la generación en tiempo real. ⚡🔄 131 | 132 | ### Tamaños de imagen soportados 📏🖼️ 133 | 134 | Flux-API-Worker soporta los siguientes tamaños y relaciones de aspecto predefinidos: 135 | 136 | - 1:1 (1024x1024) - Tamaño predeterminado 🟦 137 | - 1:2 (512x1024) 📱 138 | - 3:2 (768x512) 🖼️ 139 | - 3:4 (768x1024) 📱 140 | - 16:9 (1024x576) 🖥️ 141 | - 9:16 (576x1024) 📱 142 | 143 | Para especificar un tamaño en particular, simplemente añada la relación correspondiente después del prompt, por ejemplo: 144 | 145 | ``` 146 | "Un gato adorable 16:9" 147 | ``` 148 | 149 | Si no se especifica un tamaño, el sistema generará por defecto una imagen de 1:1 (1024x1024). 🎛️🔧 150 | 151 | ### Soporte para Compartir Recursos de Origen Cruzado (CORS) 🌍🔓 152 | 153 | Flux-API-Worker soporta CORS, permitiendo el acceso a la API desde aplicaciones web en diferentes dominios. Esto significa que puede llamar a la API directamente desde aplicaciones JavaScript frontend sin encontrar problemas de origen cruzado. 🔗🚫🚧 154 | 155 | ### Uso en aplicaciones de terceros 🔗🔌 156 | 157 | Flux-API-Worker se puede integrar fácilmente en diversas aplicaciones como NextWeb, ChatBox, etc. Al configurar en estas aplicaciones: 158 | 159 | 1. Establezca la dirección de la API como la URL de su Worker (endpoint de completado de chat). 🔗 160 | 2. Ingrese la CLAVE API que configuró. 🔑 161 | 3. No es necesario preocuparse por la configuración del System Message proporcionada por la aplicación, ya que Flux-API-Worker utiliza un System Message incorporado. 💬🚫 162 | 163 | > Nota: Flux-API-Worker ha eliminado la funcionalidad de contexto, generando una nueva imagen única en cada llamada. 🆕🖼️ 164 | 165 | ### Formato de respuesta 📤📊 166 | 167 | Ejemplo de respuesta no streaming: 168 | 169 | ```json 170 | { 171 | "id": "chatcmpl-1234567890", 172 | "created": 1677649420, 173 | "model": "@cf/black-forest-labs/flux-1-schnell", 174 | "object": "chat.completion", 175 | "choices": [ 176 | { 177 | "index": 0, 178 | "message": { 179 | "role": "assistant", 180 | "content": "🎨 Prompt original: Un gato adorable 3:2\n💬 Modelo de generación de prompts: Prompt Original\n🌐 Prompt optimizado: Un gato adorable\n📐 Especificaciones de la imagen: 768x512\n🌟 ¡Imagen generada con éxito!\nAquí está el resultado:\n\n![Imagen generada](https://url-de-su-worker.workers.dev/image/12345)" 181 | }, 182 | "finish_reason": "stop" 183 | } 184 | ], 185 | "usage": { 186 | "prompt_tokens": 20, 187 | "completion_tokens": 100, 188 | "total_tokens": 120 189 | } 190 | } 191 | ``` 192 | 193 | ## Consideraciones importantes ⚠️🚨 194 | 195 | - Asegúrese de que todas las variables de entorno necesarias estén correctamente configuradas. ✅🔧 196 | - La clave API debe mantenerse segura y no exponerse en el código del cliente. 🔒🙈 197 | - Las imágenes tienen un tiempo de expiración en el almacenamiento KV (30 minutos por defecto), guarde las imágenes importantes a tiempo. ⏳💾 198 | - Si la función de optimización de prompts está habilitada, asegúrese de que la API externa esté configurada correctamente. 🌐🔧 199 | - Al usar respuestas en streaming, asegúrese de que su cliente pueda manejar correctamente los Server-Sent Events. 🌊📡 200 | 201 | ## Solución de problemas 🔧🚑 202 | 203 | 1. Si encuentra errores de no autorización, verifique que la clave API esté correctamente configurada y utilizada. 🔑❓ 204 | 2. Si la generación de imágenes falla, verifique que el Token de API de Cloudflare tenga los permisos correctos. 🎟️🔍 205 | 3. Si la optimización de prompts no funciona, asegúrese de que `PROMPT_OPTIMIZATION` esté configurado como 'true' y que la API externa esté correctamente configurada. 🌐🔧 206 | 4. Si recibe un error 404, asegúrese de estar accediendo a la ruta de endpoint correcta. 🔍🚷 207 | 5. Para otros errores, revise los registros del Worker para obtener información de error más detallada. 📋🔬 208 | 209 | ## Personalización adicional 🛠️🎨 210 | 211 | Puede modificar el código del Worker para optimizar aún más la funcionalidad de la API, por ejemplo: 212 | 213 | - Ajustar los tamaños de imagen y relaciones de aspecto soportados 📏✂️ 214 | - Modificar los mensajes del sistema incorporados para cambiar el comportamiento de generación de prompts 💬🔄 215 | - Agregar mecanismos adicionales de manejo de errores y registro 🚨📊 216 | - Implementar límites de velocidad personalizados u otras medidas de seguridad 🛡️⏱️ 217 | 218 | Espero que este README le ayude a desplegar y utilizar rápidamente Flux-API-Worker. Si tiene alguna pregunta o necesita más ayuda, no dude en ponerse en contacto conmigo. 💌👨‍💻👩‍💻 219 | 220 | Si este repositorio le ha sido útil, por favor considere darle una estrella. ⭐⭐⭐ ¡Gracias! 221 | -------------------------------------------------------------------------------- /docs/README.fr.md: -------------------------------------------------------------------------------- 1 | # Flux-API-Worker - README 📘🎨🤖 2 | 3 | [English](../README.md) | [简体中文](./README.zh-cn.md) | [繁體中文](./README.zh-hant.md) | [日本語](./README.ja.md) | [Español](./README.es.md) | [Français](./README.fr.md) | [Русский](./README.ru.md) | [Deutsch](./README.de.md) 4 | 5 | ## Introduction 🌟💡 6 | 7 | Flux-API-Worker est un service de génération d'images IA déployé sur Cloudflare Worker. Il utilise le modèle Flux fourni par Cloudflare pour générer des images et offre une interface API efficace pour traiter les requêtes. Ce service peut être facilement intégré dans diverses applications pour fournir aux utilisateurs de puissantes capacités de génération d'images IA. ✨🖼️🚀 8 | 9 | ## Caractéristiques 🚀🌈 10 | 11 | - 🎨 Génération d'images à partir de prompts personnalisés 12 | - 🌐 Fonction d'optimisation des prompts en option 13 | - 📐 Prise en charge de plusieurs tailles et ratios d'image prédéfinis 14 | - 💾 Stockage des images générées avec Cloudflare KV 15 | - 🔄 Prise en charge des réponses en continu (streaming) et non-streaming 16 | - 🔒 Messages système intégrés pour assurer une qualité de sortie cohérente 17 | - 🌍 Prise en charge du partage de ressources entre origines (CORS) 18 | 19 | ## Démarrage rapide 🏃‍♂️💨 20 | 21 | ### Déploiement dans le tableau de bord Cloudflare 🖥️🛠️ 22 | 23 | 1. Connectez-vous à votre compte Cloudflare et accédez à la page Workers. 👨‍💻👩‍💻 24 | 2. Cliquez sur le bouton "Créer un service". 🆕 25 | 3. Nommez votre Worker, par exemple "flux-api". ✏️ 26 | 4. Collez le code du Worker fourni dans l'éditeur. 📋 27 | 5. Cliquez sur le bouton "Enregistrer et déployer". 🚀 28 | 29 | ### Configuration des variables d'environnement ⚙️🔧 30 | 31 | Dans la page des paramètres du Worker, trouvez la section "Variables d'environnement" et ajoutez les variables suivantes : 32 | 33 | ## Liste des variables d'environnement 📋🔑 34 | 35 | | Nom de la variable | Description | Type | Exemple | Valeur par défaut | 36 | |--------------------|-------------|------|---------|-------------------| 37 | | `API_KEY` | Clé d'authentification API 🔐 | Chaîne | `"votre-clé-api-complexe-ici"` | - | 38 | | `CF_ACCOUNT_ID` | ID du compte Cloudflare 🆔 | Chaîne | `"1a2b3c4d5e6f7g8h9i0j"` | - | 39 | | `CF_API_TOKEN` | Jeton API Cloudflare 🎟️ | Chaîne | `"votre-jeton-api-cloudflare"` | - | 40 | | `PROMPT_OPTIMIZATION` | Activer l'optimisation du prompt 🌐 | Chaîne | `"true"` ou `"false"` | - | 41 | | `EXTERNAL_API_BASE` | URL de base de l'API externe 🔗 | Chaîne | `"https://api.service-externe.com"` | - | 42 | | `EXTERNAL_MODEL` | Nom du modèle d'optimisation externe 🤖 | Chaîne | `"gpt-3.5-turbo"` | - | 43 | | `EXTERNAL_API_KEY` | Clé d'accès pour l'API externe 🗝️ | Chaîne | `"votre-clé-api-externe"` | - | 44 | | `FLUX_NUM_STEPS` | Nombre d'étapes pour le modèle Flux 🚶 | Entier | `"4"` | 4 | 45 | | `IMAGE_EXPIRATION` | Durée d'expiration des images dans KV (secondes) ⏳ | Entier | `"1800"` | 1800 | 46 | 47 | Assurez-vous de configurer correctement ces variables dans les paramètres des variables d'environnement de votre Cloudflare Worker. Pour les variables ayant des valeurs par défaut, vous pouvez conserver ces valeurs si aucun changement n'est nécessaire. 🔧✅ 48 | 49 | > Note : Pour des raisons de sécurité, définissez une chaîne complexe pour `API_KEY`. Elle sera utilisée pour valider la légitimité des appels API. 🔒🛡️ 50 | 51 | ### Création d'un espace de noms KV 🗄️📦 52 | 53 | 1. Dans le tableau de bord Cloudflare, allez sur la page "Workers". 🖥️ 54 | 2. Cliquez sur l'onglet "KV". 📑 55 | 3. Créez un nouvel espace de noms appelé "FLUX_CF_KV". 🆕 56 | 4. Dans les paramètres du Worker, liez cet espace de noms KV à la variable `FLUX_CF_KV`. 🔗 57 | 58 | ## Points d'accès API et fonctionnalités 🌐🛠️ 59 | 60 | ### 1. Page d'accueil 👋 61 | 62 | L'accès à la racine du Worker (`https://..workers.dev/`) affichera une page d'accueil confirmant que le service API est opérationnel. ✅🏠 63 | 64 | ### 2. Point d'accès de complétion de chat 💬 65 | 66 | Point d'accès principal pour la génération d'images : 67 | ``` 68 | https://..workers.dev/v1/chat/completions 69 | ``` 70 | 🎨✨ 71 | 72 | ### 3. Point d'accès d'information sur le modèle ℹ️ 73 | 74 | Pour obtenir des informations sur les modèles disponibles : 75 | ``` 76 | https://..workers.dev/v1/models 77 | ``` 78 | Ce point d'accès renvoie des informations sur le modèle Flux actuellement utilisé. 🤖📊 79 | 80 | ### 4. Point d'accès de récupération d'image 🖼️ 81 | 82 | Pour récupérer une image générée : 83 | ``` 84 | https://..workers.dev/image/{image_key} 85 | ``` 86 | 📥🎭 87 | 88 | ## Guide d'utilisation 📖🧭 89 | 90 | ### Génération d'images 🖼️🎨 91 | 92 | Envoyez une requête POST au point d'accès de complétion de chat avec le format suivant : 93 | 94 | ```json 95 | { 96 | "messages": [ 97 | { 98 | "role": "user", 99 | "content": "Un chat mignon 3:2" 100 | } 101 | ], 102 | "stream": false 103 | } 104 | ``` 105 | 106 | Les en-têtes de requête doivent inclure : 107 | 108 | ``` 109 | Authorization: Bearer VOTRE_CLE_API 110 | Content-Type: application/json 111 | ``` 112 | 113 | > Important : Remplacez `VOTRE_CLE_API` par la valeur de `API_KEY` que vous avez définie dans les variables d'environnement. 🔑🔄 114 | 115 | ### Réponse en continu 🌊📡 116 | 117 | Si vous souhaitez recevoir une réponse en continu, définissez le paramètre `stream` sur `true` : 118 | 119 | ```json 120 | { 121 | "messages": [ 122 | { 123 | "role": "user", 124 | "content": "Un chat mignon 3:2" 125 | } 126 | ], 127 | "stream": true 128 | } 129 | ``` 130 | 131 | Les réponses en continu seront renvoyées au format Server-Sent Events (SSE), permettant d'obtenir la progression de la génération en temps réel. ⚡🔄 132 | 133 | ### Tailles d'image prises en charge 📏🖼️ 134 | 135 | Flux-API-Worker prend en charge les tailles et ratios d'image prédéfinis suivants : 136 | 137 | - 1:1 (1024x1024) - Taille par défaut 🟦 138 | - 1:2 (512x1024) 📱 139 | - 3:2 (768x512) 🖼️ 140 | - 3:4 (768x1024) 📱 141 | - 16:9 (1024x576) 🖥️ 142 | - 9:16 (576x1024) 📱 143 | 144 | Pour spécifier une taille particulière, ajoutez simplement le ratio correspondant après le prompt, par exemple : 145 | 146 | ``` 147 | "Un chat mignon 16:9" 148 | ``` 149 | 150 | Si aucune taille n'est spécifiée, le système générera par défaut une image de 1:1 (1024x1024). 🎛️🔧 151 | 152 | ### Prise en charge du partage de ressources entre origines (CORS) 🌍🔓 153 | 154 | Flux-API-Worker prend en charge CORS, permettant l'accès à l'API depuis des applications web sur différents domaines. Cela signifie que vous pouvez appeler l'API directement depuis des applications JavaScript frontend sans rencontrer de problèmes de cross-origin. 🔗🚫🚧 155 | 156 | ### Utilisation dans des applications tierces 🔗🔌 157 | 158 | Flux-API-Worker peut être facilement intégré dans diverses applications telles que NextWeb, ChatBox, etc. Lors de la configuration dans ces applications : 159 | 160 | 1. Définissez l'adresse API sur l'URL de votre Worker (point d'accès de complétion de chat). 🔗 161 | 2. Entrez l'API KEY que vous avez définie. 🔑 162 | 3. Inutile de tenir compte des paramètres System Message fournis par l'application, car le Flux-API-Worker utilise un System Message intégré. 💬🚫 163 | 164 | > Note : Flux-API-Worker a supprimé la fonctionnalité de contexte, chaque appel générera une nouvelle image unique. 🆕🖼️ 165 | 166 | ### Format de réponse 📤📊 167 | 168 | Exemple de réponse non-streaming : 169 | 170 | ```json 171 | { 172 | "id": "chatcmpl-1234567890", 173 | "created": 1677649420, 174 | "model": "@cf/black-forest-labs/flux-1-schnell", 175 | "object": "chat.completion", 176 | "choices": [ 177 | { 178 | "index": 0, 179 | "message": { 180 | "role": "assistant", 181 | "content": "🎨 Prompt original : Un chat mignon 3:2\n💬 Modèle de génération de prompt : Original Prompt\n🌐 Prompt optimisé : Un chat mignon\n📐 Spécifications de l'image : 768x512\n🌟 Image générée avec succès !\nVoici le résultat :\n\n![Image générée](https://url-de-votre-worker.workers.dev/image/12345)" 182 | }, 183 | "finish_reason": "stop" 184 | } 185 | ], 186 | "usage": { 187 | "prompt_tokens": 20, 188 | "completion_tokens": 100, 189 | "total_tokens": 120 190 | } 191 | } 192 | ``` 193 | 194 | ## Points importants ⚠️🚨 195 | 196 | - Assurez-vous que toutes les variables d'environnement nécessaires sont correctement configurées. ✅🔧 197 | - La clé API doit être conservée en sécurité et ne pas être exposée dans le code client. 🔒🙈 198 | - Les images ont une durée d'expiration dans le stockage KV (30 minutes par défaut), sauvegardez rapidement les images importantes. ⏳💾 199 | - Si la fonctionnalité d'optimisation des prompts est activée, assurez-vous que l'API externe est correctement configurée. 🌐🔧 200 | - Lors de l'utilisation des réponses en continu, assurez-vous que votre client peut traiter correctement les Server-Sent Events. 🌊📡 201 | 202 | ## Dépannage 🔧🚑 203 | 204 | 1. En cas d'erreur d'autorisation, vérifiez que la clé API est correctement définie et utilisée. 🔑❓ 205 | 2. Si la génération d'image échoue, vérifiez que le jeton API Cloudflare a les bonnes permissions. 🎟️🔍 206 | 3. Si l'optimisation des prompts ne fonctionne pas, vérifiez que `PROMPT_OPTIMIZATION` est réglé sur 'true' et que l'API externe est correctement configurée. 🌐🔧 207 | 4. Si vous recevez une erreur 404, assurez-vous d'accéder au bon chemin de point d'accès. 🔍🚷 208 | 5. Pour d'autres erreurs, consultez les logs du Worker pour obtenir des informations d'erreur plus détaillées. 📋🔬 209 | 210 | ## Personnalisation avancée 🛠️🎨 211 | 212 | Vous pouvez modifier le code du Worker pour optimiser davantage les fonctionnalités de l'API, par exemple : 213 | 214 | - Ajuster les tailles et ratios d'image pris en charge 📏✂️ 215 | - Modifier les messages système intégrés pour changer le comportement de génération de prompts 💬🔄 216 | - Ajouter des mécanismes supplémentaires de gestion des erreurs et de journalisation 🚨📊 217 | - Implémenter des limites de taux personnalisées ou d'autres mesures de sécurité 🛡️⏱️ 218 | 219 | J'espère que ce README vous aidera à déployer et utiliser rapidement Flux-API-Worker. Si vous avez des questions ou besoin d'aide supplémentaire, n'hésitez pas à me contacter. 💌👨‍💻👩‍💻 220 | 221 | Si vous trouvez ce repo utile, n'hésitez pas à lui donner une étoile. ⭐⭐⭐ Merci ! 222 | -------------------------------------------------------------------------------- /docs/README.ja.md: -------------------------------------------------------------------------------- 1 | # Flux-API-Worker - README 📘🎨🤖 2 | 3 | [English](../README.md) | [简体中文](./README.zh-cn.md) | [繁體中文](./README.zh-hant.md) | [日本語](./README.ja.md) | [Español](./README.es.md) | [Français](./README.fr.md) | [Русский](./README.ru.md) | [Deutsch](./README.de.md) 4 | 5 | ## はじめに 🌟💡 6 | 7 | Flux-API-WorkerはCloudflare Worker上にデプロイされたAI画像生成サービスです。CloudflareのFluxモデルを利用して画像を生成し、効率的なAPIインターフェイスを提供します。このサービスは様々なアプリケーションに簡単に統合でき、ユーザーに強力なAI画像生成機能を提供します。✨🖼️🚀 8 | 9 | ## 主な機能 🚀🌈 10 | 11 | - 🎨 カスタムプロンプトによる画像生成 12 | - 🌐 オプションのプロンプト最適化機能 13 | - 📐 複数のプリセット画像サイズとアスペクト比 14 | - 💾 Cloudflare KVを使用した生成画像の保存 15 | - 🔄 ストリーミングおよび非ストリーミングレスポンスのサポート 16 | - 🔒 一貫した出力品質を確保する内蔵システムメッセージ 17 | - 🌍 クロスオリジンリソース共有(CORS)サポート 18 | 19 | ## クイックスタート 🏃‍♂️💨 20 | 21 | ### Cloudflare Dashboardでのデプロイ 🖥️🛠️ 22 | 23 | 1. Cloudflareアカウントにログインし、Workersページに移動します。👨‍💻👩‍💻 24 | 2. 「サービスを作成」ボタンをクリックします。🆕 25 | 3. Workerに名前を付けます(例:「flux-api」)。✏️ 26 | 4. エディタに提供されたWorkerコードを貼り付けます。📋 27 | 5. 「保存してデプロイ」ボタンをクリックします。🚀 28 | 29 | ### 環境変数の設定 ⚙️🔧 30 | 31 | Workerの設定ページで、「環境変数」セクションを見つけ、以下の変数を追加します: 32 | 33 | ## 環境変数リスト 📋🔑 34 | 35 | | 変数名 | 説明 | 型 | 例 | デフォルト値 | 36 | |--------|------|------|------|--------| 37 | | `API_KEY` | API認証キー 🔐 | 文字列 | `"your-complex-api-key-here"` | - | 38 | | `CF_ACCOUNT_ID` | Cloudflareアカウント ID 🆔 | 文字列 | `"1a2b3c4d5e6f7g8h9i0j"` | - | 39 | | `CF_API_TOKEN` | Cloudflare APIトークン 🎟️ | 文字列 | `"your-cloudflare-api-token"` | - | 40 | | `PROMPT_OPTIMIZATION` | プロンプト最適化を有効にするか 🌐 | 文字列 | `"true"` または `"false"` | - | 41 | | `EXTERNAL_API_BASE` | 外部APIのベースURL 🔗 | 文字列 | `"https://api.external-service.com"` | - | 42 | | `EXTERNAL_MODEL` | 外部最適化モデル名 🤖 | 文字列 | `"gpt-3.5-turbo"` | - | 43 | | `EXTERNAL_API_KEY` | 外部APIのアクセスキー 🗝️ | 文字列 | `"your-external-api-key"` | - | 44 | | `FLUX_NUM_STEPS` | Fluxモデルのステップ数 🚶 | 整数 | `"4"` | 4 | 45 | | `IMAGE_EXPIRATION` | KVでの画像の有効期限(秒)⏳ | 整数 | `"1800"` | 1800 | 46 | 47 | Cloudflare Workerの環境変数設定でこれらの変数を正しく設定してください。デフォルト値がある変数は、変更が必要ない場合はそのままにしておいてもかまいません。🔧✅ 48 | 49 | > 注意:セキュリティを確保するため、`API_KEY`には複雑な文字列を設定してください。これはAPI呼び出しの正当性を検証するために使用されます。🔒🛡️ 50 | 51 | ### KV名前空間の作成 🗄️📦 52 | 53 | 1. Cloudflare Dashboardで、「Workers」ページに移動します。🖥️ 54 | 2. 「KV」タブをクリックします。📑 55 | 3. "FLUX_CF_KV"という名前で新しい名前空間を作成します。🆕 56 | 4. Workerの設定で、このKV名前空間を`FLUX_CF_KV`変数にバインドします。🔗 57 | 58 | ## APIエンドポイントと機能 🌐🛠️ 59 | 60 | ### 1. ウェルカムページ 👋 61 | 62 | Workerのルートパス(`https://..workers.dev/`)にアクセスすると、APIサービスが稼働していることを確認するウェルカムページが表示されます。✅🏠 63 | 64 | ### 2. チャット完了エンドポイント 💬 65 | 66 | 主要な画像生成エンドポイント: 67 | ``` 68 | https://..workers.dev/v1/chat/completions 69 | ``` 70 | 🎨✨ 71 | 72 | ### 3. モデル情報エンドポイント ℹ️ 73 | 74 | 利用可能なモデル情報の取得: 75 | ``` 76 | https://..workers.dev/v1/models 77 | ``` 78 | このエンドポイントは現在使用中のFluxモデルの情報を返します。🤖📊 79 | 80 | ### 4. 画像取得エンドポイント 🖼️ 81 | 82 | 生成された画像の取得: 83 | ``` 84 | https://..workers.dev/image/{image_key} 85 | ``` 86 | 📥🎭 87 | 88 | ## 使用ガイド 📖🧭 89 | 90 | ### 画像の生成 🖼️🎨 91 | 92 | チャット完了エンドポイントにPOSTリクエストを送信します。フォーマットは以下の通りです: 93 | 94 | ```json 95 | { 96 | "messages": [ 97 | { 98 | "role": "user", 99 | "content": "かわいい猫 3:2" 100 | } 101 | ], 102 | "stream": false 103 | } 104 | ``` 105 | 106 | リクエストヘッダーには以下を含める必要があります: 107 | 108 | ``` 109 | Authorization: Bearer YOUR_API_KEY 110 | Content-Type: application/json 111 | ``` 112 | 113 | > 重要:`YOUR_API_KEY`を環境変数で設定した`API_KEY`の値に置き換えてください。🔑🔄 114 | 115 | ### ストリーミングレスポンス 🌊📡 116 | 117 | ストリーミングレスポンスを受信したい場合は、`stream`パラメータを`true`に設定します: 118 | 119 | ```json 120 | { 121 | "messages": [ 122 | { 123 | "role": "user", 124 | "content": "かわいい猫 3:2" 125 | } 126 | ], 127 | "stream": true 128 | } 129 | ``` 130 | 131 | ストリーミングレスポンスはServer-Sent Events(SSE)形式で返され、生成の進捗をリアルタイムで取得できます。⚡🔄 132 | 133 | ### サポートされる画像サイズ 📏🖼️ 134 | 135 | Flux-API-Workerは以下のプリセット画像サイズとアスペクト比をサポートしています: 136 | 137 | - 1:1(1024x1024)- デフォルトサイズ 🟦 138 | - 1:2 (512x1024) 📱 139 | - 3:2 (768x512) 🖼️ 140 | - 3:4 (768x1024) 📱 141 | - 16:9 (1024x576) 🖥️ 142 | - 9:16 (576x1024) 📱 143 | 144 | 特定のサイズを指定するには、プロンプトの後に対応する比率を追加するだけです。例えば: 145 | 146 | ``` 147 | "かわいい猫 16:9" 148 | ``` 149 | 150 | サイズが指定されていない場合、システムはデフォルトで1:1(1024x1024)の画像を生成します。🎛️🔧 151 | 152 | ### クロスオリジンリソース共有(CORS)サポート 🌍🔓 153 | 154 | Flux-API-WorkerはCORSをサポートしており、異なるドメインのウェブアプリケーションからAPIにアクセスすることができます。これにより、フロントエンドのJavaScriptアプリケーションで直接APIを呼び出すことができ、クロスドメインの問題に遭遇することはありません。🔗🚫🚧 155 | 156 | ### サードパーティアプリケーションでの使用 🔗🔌 157 | 158 | Flux-API-WorkerはNextWeb、ChatBoxなど、さまざまなアプリケーションに簡単に統合できます。これらのアプリケーションで設定する際: 159 | 160 | 1. APIアドレスをあなたのWorker URL(チャット完了エンドポイント)に設定します。🔗 161 | 2. 設定したAPI KEYを入力します。🔑 162 | 3. アプリが提供するSystem Messageの設定を気にする必要はありません。Flux-API-Workerは組み込みのSystem Messageを使用します。💬🚫 163 | 164 | > 注意:Flux-API-Workerはコンテキスト機能を削除しました。毎回の呼び出しで新しいユニークな画像が生成されます。🆕🖼️ 165 | 166 | ### レスポンス形式 📤📊 167 | 168 | 非ストリーミングレスポンスの例: 169 | 170 | ```json 171 | { 172 | "id": "chatcmpl-1234567890", 173 | "created": 1677649420, 174 | "model": "@cf/black-forest-labs/flux-1-schnell", 175 | "object": "chat.completion", 176 | "choices": [ 177 | { 178 | "index": 0, 179 | "message": { 180 | "role": "assistant", 181 | "content": "🎨 元のプロンプト:かわいい猫 3:2\n💬 プロンプト生成モデル:オリジナルプロンプト\n🌐 最適化されたプロンプト:かわいい猫\n📐 画像仕様:768x512\n🌟 画像生成成功!\n結果は以下の通りです:\n\n![生成された画像](https://your-worker-url.workers.dev/image/12345)" 182 | }, 183 | "finish_reason": "stop" 184 | } 185 | ], 186 | "usage": { 187 | "prompt_tokens": 20, 188 | "completion_tokens": 100, 189 | "total_tokens": 120 190 | } 191 | } 192 | ``` 193 | 194 | ## 注意事項 ⚠️🚨 195 | 196 | - 必要なすべての環境変数が正しく設定されていることを確認してください。✅🔧 197 | - APIキーは適切に管理し、クライアント側のコードで露出しないようにしてください。🔒🙈 198 | - 画像はKVストレージで有効期限があります(デフォルトは30分)。重要な画像はタイムリーに保存してください。⏳💾 199 | - プロンプト最適化機能が有効の場合、外部APIが正しく設定されていることを確認してください。🌐🔧 200 | - ストリーミングレスポンスを使用する場合は、クライアントがServer-Sent Eventsを正しく処理できることを確認してください。🌊📡 201 | 202 | ## トラブルシューティング 🔧🚑 203 | 204 | 1. 認証エラーが発生した場合は、APIキーが正しく設定され使用されているか確認してください。🔑❓ 205 | 2. 画像生成が失敗した場合は、Cloudflare API Tokenが正しい権限を持っているか確認してください。🎟️🔍 206 | 3. プロンプト最適化が動作しない場合、`PROMPT_OPTIMIZATION` が 'true' に設定されていること、および外部APIの設定が正しいことを確認してください。🌐🔧 207 | 4. 404エラーを受け取った場合は、正しいエンドポイントパスにアクセスしていることを確認してください。🔍🚷 208 | 5. その他のエラーについては、Workerのログでより詳細なエラー情報を確認してください。📋🔬 209 | 210 | ## さらなるカスタマイズ 🛠️🎨 211 | 212 | Workerコードを修正することで、APIの機能をさらに最適化することができます。例えば: 213 | 214 | - サポートする画像サイズとアスペクト比の調整 📏✂️ 215 | - プロンプト生成の動作を変更するための内蔵システムメッセージの修正 💬🔄 216 | - 追加のエラー処理とログ記録メカニズムの実装 🚨📊 217 | - カスタムのレート制限やその他のセキュリティ対策の実装 🛡️⏱️ 218 | 219 | このREADMEがFlux-API-Workerの迅速な展開と使用に役立つことを願っています。質問や更なる支援が必要な場合は、お気軽にお問い合わせください。💌👨‍💻👩‍💻 220 | 221 | このリポジトリがお役に立てた場合は、スターをつけていただけると幸いです。⭐⭐⭐ ありがとうございます! 222 | -------------------------------------------------------------------------------- /docs/README.ru.md: -------------------------------------------------------------------------------- 1 | # Flux-API-Worker - README 📘🎨🤖 2 | 3 | [English](../README.md) | [简体中文](./README.zh-cn.md) | [繁體中文](./README.zh-hant.md) | [日本語](./README.ja.md) | [Español](./README.es.md) | [Français](./README.fr.md) | [Русский](./README.ru.md) | [Deutsch](./README.de.md) 4 | 5 | ## Введение 🌟💡 6 | 7 | Flux-API-Worker - это сервис генерации изображений с помощью ИИ, развернутый на Cloudflare Worker. Он использует модель Flux, предоставляемую Cloudflare, для создания изображений и предлагает эффективный API-интерфейс для обработки запросов. Этот сервис можно легко интегрировать в различные приложения, предоставляя пользователям мощные возможности генерации изображений с помощью ИИ. ✨🖼️🚀 8 | 9 | ## Особенности 🚀🌈 10 | 11 | - 🎨 Поддержка пользовательских подсказок для генерации изображений 12 | - 🌐 Опциональная функция оптимизации запросов 13 | - 📐 Поддержка различных предустановленных размеров и соотношений сторон изображений 14 | - 💾 Хранение сгенерированных изображений с использованием Cloudflare KV 15 | - 🔄 Поддержка потоковых и непотоковых ответов 16 | - 🔒 Встроенные системные сообщения для обеспечения стабильного качества вывода 17 | - 🌍 Поддержка Cross-Origin Resource Sharing (CORS) 18 | 19 | ## Быстрый старт 🏃‍♂️💨 20 | 21 | ### Развертывание в Cloudflare Dashboard 🖥️🛠️ 22 | 23 | 1. Войдите в свою учетную запись Cloudflare и перейдите на страницу Workers. 👨‍💻👩‍💻 24 | 2. Нажмите кнопку "Создать сервис". 🆕 25 | 3. Назовите ваш Worker, например, "flux-api". ✏️ 26 | 4. Вставьте предоставленный код Worker в редактор. 📋 27 | 5. Нажмите кнопку "Сохранить и развернуть". 🚀 28 | 29 | ### Настройка переменных окружения ⚙️🔧 30 | 31 | На странице настроек Worker найдите раздел "Переменные окружения" и добавьте следующие переменные: 32 | 33 | ## Список переменных окружения 📋🔑 34 | 35 | | Имя переменной | Описание | Тип | Пример | По умолчанию | 36 | |----------------|----------|-----|--------|--------------| 37 | | `API_KEY` | Ключ аутентификации API 🔐 | Строка | `"ваш-сложный-ключ-api"` | - | 38 | | `CF_ACCOUNT_ID` | ID аккаунта Cloudflare 🆔 | Строка | `"1a2b3c4d5e6f7g8h9i0j"` | - | 39 | | `CF_API_TOKEN` | Токен API Cloudflare 🎟️ | Строка | `"ваш-токен-api-cloudflare"` | - | 40 | | `PROMPT_OPTIMIZATION` | Включить оптимизацию запроса 🌐 | Строка | `"true"` или `"false"` | - | 41 | | `EXTERNAL_API_BASE` | Базовый URL внешнего API 🔗 | Строка | `"https://api.external-service.com"` | - | 42 | | `EXTERNAL_MODEL` | Название внешней модели оптимизации 🤖 | Строка | `"gpt-3.5-turbo"` | - | 43 | | `EXTERNAL_API_KEY` | Ключ доступа к внешнему API 🗝️ | Строка | `"ваш-внешний-ключ-api"` | - | 44 | | `FLUX_NUM_STEPS` | Количество шагов модели Flux 🚶 | Целое число | `"4"` | 4 | 45 | | `IMAGE_EXPIRATION` | Время хранения изображений в KV (в секундах) ⏳ | Целое число | `"1800"` | 1800 | 46 | 47 | Убедитесь, что правильно настроили эти переменные в настройках переменных окружения Cloudflare Worker. Для переменных с значениями по умолчанию, если изменения не требуются, можно оставить настройки по умолчанию. 🔧✅ 48 | 49 | > Примечание: Для обеспечения безопасности установите сложную строку для `API_KEY`. Она будет использоваться для проверки подлинности вызовов API. 🔒🛡️ 50 | 51 | ### Создание пространства имен KV 🗄️📦 52 | 53 | 1. В панели управления Cloudflare перейдите на страницу "Workers". 🖥️ 54 | 2. Нажмите на вкладку "KV". 📑 55 | 3. Создайте новое пространство имен и назовите его "FLUX_CF_KV". 🆕 56 | 4. В настройках Worker привяжите это пространство имен KV к переменной `FLUX_CF_KV`. 🔗 57 | 58 | ## Конечные точки API и функциональность 🌐🛠️ 59 | 60 | ### 1. Страница приветствия 👋 61 | 62 | При доступе к корневому пути Worker (`https://<имя_вашего_worker>.<ваш_поддомен>.workers.dev/`) будет отображаться страница приветствия, подтверждающая работу API-сервиса. ✅🏠 63 | 64 | ### 2. Конечная точка завершения чата 💬 65 | 66 | Основная конечная точка для генерации изображений: 67 | ``` 68 | https://<имя_вашего_worker>.<ваш_поддомен>.workers.dev/v1/chat/completions 69 | ``` 70 | 🎨✨ 71 | 72 | ### 3. Конечная точка информации о модели ℹ️ 73 | 74 | Получение информации о доступных моделях: 75 | ``` 76 | https://<имя_вашего_worker>.<ваш_поддомен>.workers.dev/v1/models 77 | ``` 78 | Эта конечная точка возвращает информацию о текущей используемой модели Flux. 🤖📊 79 | 80 | ### 4. Конечная точка получения изображения 🖼️ 81 | 82 | Получение сгенерированного изображения: 83 | ``` 84 | https://<имя_вашего_worker>.<ваш_поддомен>.workers.dev/image/{ключ_изображения} 85 | ``` 86 | 📥🎭 87 | 88 | ## Руководство по использованию 📖🧭 89 | 90 | ### Генерация изображения 🖼️🎨 91 | 92 | Отправьте POST-запрос на конечную точку завершения чата в следующем формате: 93 | 94 | ```json 95 | { 96 | "messages": [ 97 | { 98 | "role": "user", 99 | "content": "Милый котенок 3:2" 100 | } 101 | ], 102 | "stream": false 103 | } 104 | ``` 105 | 106 | Заголовки запроса должны включать: 107 | 108 | ``` 109 | Authorization: Bearer ВАШ_КЛЮЧ_API 110 | Content-Type: application/json 111 | ``` 112 | 113 | > Важно: Замените `ВАШ_КЛЮЧ_API` на значение `API_KEY`, которое вы установили в переменных окружения. 🔑🔄 114 | 115 | ### Потоковый ответ 🌊📡 116 | 117 | Если вы хотите получить потоковый ответ, установите параметр `stream` в значение `true`: 118 | 119 | ```json 120 | { 121 | "messages": [ 122 | { 123 | "role": "user", 124 | "content": "Милый котенок 3:2" 125 | } 126 | ], 127 | "stream": true 128 | } 129 | ``` 130 | 131 | Потоковый ответ будет возвращен в формате Server-Sent Events (SSE), что позволяет получать прогресс генерации в реальном времени. ⚡🔄 132 | 133 | ### Поддерживаемые размеры изображений 📏🖼️ 134 | 135 | Flux-API-Worker поддерживает следующие предустановленные размеры и соотношения сторон изображений: 136 | 137 | - 1:1 (1024x1024) - размер по умолчанию 🟦 138 | - 1:2 (512x1024) 📱 139 | - 3:2 (768x512) 🖼️ 140 | - 3:4 (768x1024) 📱 141 | - 16:9 (1024x576) 🖥️ 142 | - 9:16 (576x1024) 📱 143 | 144 | Чтобы указать конкретный размер, просто добавьте соответствующее соотношение сторон после подсказки, например: 145 | 146 | ``` 147 | "Милый котенок 16:9" 148 | ``` 149 | 150 | Если размер не указан, система по умолчанию сгенерирует изображение 1:1 (1024x1024). 🎛️🔧 151 | 152 | ### Поддержка Cross-Origin Resource Sharing (CORS) 🌍🔓 153 | 154 | Flux-API-Worker поддерживает CORS, что позволяет обращаться к API из веб-приложений на разных доменах. Это означает, что вы можете напрямую вызывать API из фронтенд-приложений на JavaScript без проблем с кросс-доменными запросами. 🔗🚫🚧 155 | 156 | ### Использование в сторонних приложениях 🔗🔌 157 | 158 | Flux-API-Worker можно легко интегрировать в различные приложения, такие как NextWeb, ChatBox и другие. При настройке в этих приложениях: 159 | 160 | 1. Установите адрес API на URL вашего Worker (конечная точка завершения чата). 🔗 161 | 2. Введите установленный вами API KEY. 🔑 162 | 3. Нет необходимости учитывать настройки System Message, предоставляемые приложением, так как Flux-API-Worker использует встроенное System Message. 💬🚫 163 | 164 | > Примечание: Flux-API-Worker удалил функциональность контекста, каждый вызов будет генерировать новое уникальное изображение. 🆕🖼️ 165 | 166 | ### Формат ответа 📤📊 167 | 168 | Пример непотокового ответа: 169 | 170 | ```json 171 | { 172 | "id": "chatcmpl-1234567890", 173 | "created": 1677649420, 174 | "model": "@cf/black-forest-labs/flux-1-schnell", 175 | "object": "chat.completion", 176 | "choices": [ 177 | { 178 | "index": 0, 179 | "message": { 180 | "role": "assistant", 181 | "content": "🎨 Оригинальная подсказка: Милый котенок 3:2\n💬 Модель генерации подсказки: Original Prompt\n🌐 Оптимизированный запрос: Милый котенок\n📐 Спецификация изображения: 768x512\n🌟 Изображение успешно сгенерировано!\nВот результат:\n\n![Сгенерированное изображение](https://url-вашего-worker.workers.dev/image/12345)" 182 | }, 183 | "finish_reason": "stop" 184 | } 185 | ], 186 | "usage": { 187 | "prompt_tokens": 20, 188 | "completion_tokens": 100, 189 | "total_tokens": 120 190 | } 191 | } 192 | ``` 193 | 194 | ## Важные замечания ⚠️🚨 195 | 196 | - Убедитесь, что все необходимые переменные окружения правильно настроены. ✅🔧 197 | - API-ключ должен храниться в безопасности и не раскрываться в клиентском коде. 🔒🙈 198 | - Изображения в хранилище KV имеют срок хранения (по умолчанию 30 минут), своевременно сохраняйте важные изображения. ⏳💾 199 | - Если функция оптимизации запросов включена, убедитесь, что внешнее API настроено правильно. 🌐🔧 200 | - При использовании потокового ответа убедитесь, что ваш клиент может правильно обрабатывать Server-Sent Events. 🌊📡 201 | 202 | ## Устранение неполадок 🔧🚑 203 | 204 | 1. При появлении ошибки неавторизованного доступа проверьте правильность установки и использования API-ключа. 🔑❓ 205 | 2. Если генерация изображения не удается, убедитесь, что токен API Cloudflare имеет правильные разрешения. 🎟️🔍 206 | 3. Если оптимизация запросов не работает, убедитесь, что `PROMPT_OPTIMIZATION` установлено на 'true' и внешнее API настроено правильно. 🌐🔧 207 | 4. Если вы получаете ошибку 404, убедитесь, что обращаетесь к правильному пути конечной точки. 🔍🚷 208 | 5. Для других ошибок проверьте логи Worker для получения более подробной информации об ошибке. 📋🔬 209 | 210 | ## Дальнейшая настройка 🛠️🎨 211 | 212 | Вы можете дополнительно оптимизировать функциональность API, изменив код Worker, например: 213 | 214 | - Настроить поддерживаемые размеры и соотношения сторон изображений 📏✂️ 215 | - Изменить встроенное системное сообщение для изменения поведения генерации подсказок 💬🔄 216 | - Добавить дополнительные механизмы обработки ошибок и ведения журнала 🚨📊 217 | - Реализовать пользовательские ограничения скорости или другие меры безопасности 🛡️⏱️ 218 | 219 | Я надеюсь, что этот README поможет вам быстро развернуть и использовать Flux-API-Worker. Если у вас есть вопросы или нужна дополнительная помощь, не стесняйтесь обращаться ко мне. 💌👨‍💻👩‍💻 220 | 221 | Если вы считаете, что этот репозиторий был вам полезен, пожалуйста, поставьте ему звезду. ⭐⭐⭐ Спасибо! 222 | -------------------------------------------------------------------------------- /docs/README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # Flux-API-Worker 📘🎨🤖 2 | 3 | [English](../README.md) | [简体中文](./README.zh-cn.md) | [繁體中文](./README.zh-hant.md) | [日本語](./README.ja.md) | [Español](./README.es.md) | [Français](./README.fr.md) | [Русский](./README.ru.md) | [Deutsch](./README.de.md) 4 | 5 | ## 简介 🌟💡 6 | 7 | Flux-API-Worker 是一个部署在 Cloudflare Worker 上的 AI 图像生成服务。它利用 Cloudflare 提供的 Flux 模型来生成图像,并提供了一个高效的 API 接口来处理请求。这个服务可以轻松集成到各种应用中,为用户提供强大的 AI 图像生成能力。✨🖼️🚀 8 | 9 | ## 功能特点 🚀🌈 10 | 11 | - 🎨 支持自定义提示词生成图像 12 | - 🌐 可选的提示词优化功能 13 | - 📐 支持多种预设图像尺寸和宽高比 14 | - 💾 使用 Cloudflare KV 存储生成的图像 15 | - 🔄 支持流式和非流式响应 16 | - 🔒 内置系统消息,确保一致的输出质量 17 | - 🌍 跨源资源共享(CORS)支持 18 | 19 | ## 快速开始 🏃‍♂️💨 20 | 21 | ### 在 Cloudflare Dashboard 中部署 🖥️🛠️ 22 | 23 | 1. 登录到您的 Cloudflare 账户,进入 Workers 页面。👨‍💻👩‍💻 24 | 2. 点击 "创建服务" 按钮。🆕 25 | 3. 为您的 Worker 命名,例如 "flux-api"。✏️ 26 | 4. 在编辑器中,粘贴提供的 Worker 代码。📋 27 | 5. 点击 "保存并部署" 按钮。🚀 28 | 29 | ### 设置环境变量 ⚙️🔧 30 | 31 | 在 Worker 的设置页面中,找到 "环境变量" 部分,添加以下变量: 32 | 33 | ## 环境变量列表 📋🔑 34 | 35 | | 变量名 | 描述 | 类型 | 示例 | 默认值 | 36 | |--------|------|------|------|--------| 37 | | `API_KEY` | API 身份验证密钥 🔐 | 字符串 | `"your-complex-api-key-here"` | - | 38 | | `CF_ACCOUNT_ID` | Cloudflare 账户 ID 🆔 | 字符串 | `"1a2b3c4d5e6f7g8h9i0j"` | - | 39 | | `CF_API_TOKEN` | Cloudflare API 令牌 🎟️ | 字符串 | `"your-cloudflare-api-token"` | - | 40 | | `PROMPT_OPTIMIZATION` | 是否启用提示词优化 🌐 | 字符串 | `"true"` 或 `"false"` | - | 41 | | `EXTERNAL_API_BASE` | 外部 API 的基础 URL 🔗 | 字符串 | `"https://api.external-service.com"` | - | 42 | | `EXTERNAL_MODEL` | 外部优化模型名称 🤖 | 字符串 | `"gpt-3.5-turbo"` | - | 43 | | `EXTERNAL_API_KEY` | 外部 API 的访问密钥 🗝️ | 字符串 | `"your-external-api-key"` | - | 44 | | `FLUX_NUM_STEPS` | Flux 模型的步数 🚶 | 整数 | `"4"` | 4 | 45 | | `IMAGE_EXPIRATION` | 图像在 KV 中的过期时间(秒)⏳ | 整数 | `"1800"` | 1800 | 46 | 47 | 请确保在 Cloudflare Worker 的环境变量设置中正确配置这些变量。对于有默认值的变量,如不需更改,可保持默认设置。🔧✅ 48 | 49 | > 注意:为了保证安全,请为 `API_KEY` 设置一个复杂的字符串。这将用于验证 API 调用的合法性。🔒🛡️ 50 | 51 | ### 创建 KV 命名空间 🗄️📦 52 | 53 | 1. 在 Cloudflare Dashboard 中,转到 "Workers" 页面。🖥️ 54 | 2. 点击 "KV" 选项卡。📑 55 | 3. 创建一个新的命名空间,命名为 "FLUX_CF_KV"。🆕 56 | 4. 在 Worker 的设置中,将这个 KV 命名空间绑定到 `FLUX_CF_KV` 变量。🔗 57 | 58 | ## API 端点和功能 🌐🛠️ 59 | 60 | ### 1. 欢迎页面 👋 61 | 62 | 访问 Worker 的根路径 (`https://..workers.dev/`) 将显示一个欢迎页面,确认 API 服务正在运行。✅🏠 63 | 64 | ### 2. 聊天完成端点 💬 65 | 66 | 主要的图像生成端点: 67 | ``` 68 | https://..workers.dev/v1/chat/completions 69 | ``` 70 | 🎨✨ 71 | 72 | ### 3. 模型信息端点 ℹ️ 73 | 74 | 获取可用模型信息: 75 | ``` 76 | https://..workers.dev/v1/models 77 | ``` 78 | 这个端点返回当前使用的 Flux 模型信息。🤖📊 79 | 80 | ### 4. 图像获取端点 🖼️ 81 | 82 | 获取生成的图像: 83 | ``` 84 | https://..workers.dev/image/{image_key} 85 | ``` 86 | 📥🎭 87 | 88 | ## 使用指南 📖🧭 89 | 90 | ### 生成图像 🖼️🎨 91 | 92 | 发送 POST 请求到聊天完成端点,格式如下: 93 | 94 | ```json 95 | { 96 | "messages": [ 97 | { 98 | "role": "user", 99 | "content": "一只可爱的猫咪 3:2" 100 | } 101 | ], 102 | "stream": false 103 | } 104 | ``` 105 | 106 | 请求头必须包含: 107 | 108 | ``` 109 | Authorization: Bearer YOUR_API_KEY 110 | Content-Type: application/json 111 | ``` 112 | 113 | > 重要:请将 `YOUR_API_KEY` 替换为您在环境变量中设置的 `API_KEY` 值。🔑🔄 114 | 115 | ### 流式响应 🌊📡 116 | 117 | 如果您希望接收流式响应,将 `stream` 参数设置为 `true`: 118 | 119 | ```json 120 | { 121 | "messages": [ 122 | { 123 | "role": "user", 124 | "content": "一只可爱的猫咪 3:2" 125 | } 126 | ], 127 | "stream": true 128 | } 129 | ``` 130 | 131 | 流式响应将以 Server-Sent Events (SSE) 格式返回,允许实时获取生成进度。⚡🔄 132 | 133 | ### 支持的图像尺寸 📏🖼️ 134 | 135 | Flux-API-Worker 支持以下预设的图像尺寸和宽高比: 136 | 137 | - 1:1 (1024x1024) - 默认尺寸 🟦 138 | - 1:2 (512x1024) 📱 139 | - 3:2 (768x512) 🖼️ 140 | - 3:4 (768x1024) 📱 141 | - 16:9 (1024x576) 🖥️ 142 | - 9:16 (576x1024) 📱 143 | 144 | 要指定特定的尺寸,只需在提示词后面添加相应的比例,例如: 145 | 146 | ``` 147 | "一只可爱的猫咪 16:9" 148 | ``` 149 | 150 | 如果没有指定尺寸,系统将默认生成 1:1 (1024x1024) 的图片。🎛️🔧 151 | 152 | ### 跨源资源共享(CORS)支持 🌍🔓 153 | 154 | Flux-API-Worker 支持 CORS,允许从不同域名的网页应用程序访问 API。这意味着您可以在前端 JavaScript 应用中直接调用 API,而不会遇到跨域问题。🔗🚫🚧 155 | 156 | ### 在第三方应用中使用 🔗🔌 157 | 158 | Flux-API-Worker 可以轻松集成到各种应用中,如 NextWeb、ChatBox 等。在这些应用中配置时: 159 | 160 | 1. 将 API 地址设置为您的 Worker URL(聊天完成端点)。🔗 161 | 2. 输入您设置的 API KEY。🔑 162 | 3. 无需理会应用提供的 System Message 设置,因为 Flux-API-Worker 使用内置的 System Message。💬🚫 163 | 164 | > 注意:Flux-API-Worker 已经移除了上下文功能,每次调用都会生成新的独特图像。🆕🖼️ 165 | 166 | ### 响应格式 📤📊 167 | 168 | 非流式响应示例: 169 | 170 | ```json 171 | { 172 | "id": "chatcmpl-1234567890", 173 | "created": 1677649420, 174 | "model": "@cf/black-forest-labs/flux-1-schnell", 175 | "object": "chat.completion", 176 | "choices": [ 177 | { 178 | "index": 0, 179 | "message": { 180 | "role": "assistant", 181 | "content": "🎨 原始提示词:一只可爱的猫咪 3:2\n💬 提示词生成模型:Original Prompt\n🌐 优化后的提示词:一只可爱的猫咪\n📐 图像规格:768x512\n🌟 图像生成成功!\n以下是结果:\n\n![生成的图像](https://your-worker-url.workers.dev/image/12345)" 182 | }, 183 | "finish_reason": "stop" 184 | } 185 | ], 186 | "usage": { 187 | "prompt_tokens": 20, 188 | "completion_tokens": 100, 189 | "total_tokens": 120 190 | } 191 | } 192 | ``` 193 | 194 | ## 注意事项 ⚠️🚨 195 | 196 | - 确保所有必要的环境变量都已正确设置。✅🔧 197 | - API 密钥应当妥善保管,避免在客户端代码中暴露。🔒🙈 198 | - 图像在 KV 存储中有过期时间(默认 30 分钟),请及时保存重要的图像。⏳💾 199 | - 如启用提示词优化功能,请确保外部 API 配置正确。🌐🔧 200 | - 使用流式响应时,确保您的客户端能够正确处理 Server-Sent Events。🌊📡 201 | 202 | ## 故障排除 🔧🚑 203 | 204 | 1. 遇到未授权错误时,请检查 API 密钥是否正确设置和使用。🔑❓ 205 | 2. 图像生成失败时,请验证 Cloudflare API Token 是否具有正确的权限。🎟️🔍 206 | 3. 提示词优化不工作时,请确认 `PROMPT_OPTIMIZATION` 设置为 'true' 且外部 API 配置无误。🌐🔧 207 | 4. 如果收到 404 错误,确保您访问的是正确的端点路径。🔍🚷 208 | 5. 对于其他错误,检查 Worker 的日志以获取更详细的错误信息。📋🔬 209 | 210 | ## 进一步定制 🛠️🎨 211 | 212 | 您可以通过修改 Worker 代码来进一步优化 API 的功能,例如: 213 | 214 | - 调整支持的图像尺寸和宽高比 📏✂️ 215 | - 修改内置的系统消息以改变提示词生成的行为 💬🔄 216 | - 添加额外的错误处理和日志记录机制 🚨📊 217 | - 实现自定义的速率限制或其他安全措施 🛡️⏱️ 218 | 219 | 我希望这个 README 能协助您快速部署和使用 Flux-API-Worker。如果您有任何疑问或需要进一步的帮助,请随时与我联系。💌👨‍💻👩‍💻 220 | 221 | 如果你觉得这个repo帮到了您,请给我一个start吧。⭐⭐⭐ 谢谢 222 | -------------------------------------------------------------------------------- /docs/README.zh-hant.md: -------------------------------------------------------------------------------- 1 | # Flux-API-Worker - README 📘🎨🤖 2 | 3 | [English](../README.md) | [简体中文](./README.zh-cn.md) | [繁體中文](./README.zh-hant.md) | [日本語](./README.ja.md) | [Español](./README.es.md) | [Français](./README.fr.md) | [Русский](./README.ru.md) | [Deutsch](./README.de.md) 4 | 5 | ## 簡介 🌟💡 6 | 7 | Flux-API-Worker 是一個部署在 Cloudflare Worker 上的 AI 圖像生成服務。它利用 Cloudflare 提供的 Flux 模型來生成圖像,並提供了一個高效的 API 介面來處理請求。這個服務可以輕鬆整合到各種應用中,為用戶提供強大的 AI 圖像生成能力。✨🖼️🚀 8 | 9 | ## 功能特點 🚀🌈 10 | 11 | - 🎨 支援自定義提示詞生成圖像 12 | - 🌐 可選的提示詞優化功能 13 | - 📐 支援多種預設圖像尺寸和寬高比 14 | - 💾 使用 Cloudflare KV 儲存生成的圖像 15 | - 🔄 支援串流和非串流回應 16 | - 🔒 內置系統訊息,確保一致的輸出品質 17 | - 🌍 跨源資源共享(CORS)支援 18 | 19 | ## 快速開始 🏃‍♂️💨 20 | 21 | ### 在 Cloudflare Dashboard 中部署 🖥️🛠️ 22 | 23 | 1. 登入到您的 Cloudflare 帳戶,進入 Workers 頁面。👨‍💻👩‍💻 24 | 2. 點擊 "創建服務" 按鈕。🆕 25 | 3. 為您的 Worker 命名,例如 "flux-api"。✏️ 26 | 4. 在編輯器中,貼上提供的 Worker 代碼。📋 27 | 5. 點擊 "保存並部署" 按鈕。🚀 28 | 29 | ### 設置環境變數 ⚙️🔧 30 | 31 | 在 Worker 的設置頁面中,找到 "環境變數" 部分,添加以下變數: 32 | 33 | ## 環境變數列表 📋🔑 34 | 35 | | 變數名 | 描述 | 類型 | 示例 | 預設值 | 36 | |--------|------|------|------|--------| 37 | | `API_KEY` | API 身份驗證密鑰 🔐 | 字串 | `"your-complex-api-key-here"` | - | 38 | | `CF_ACCOUNT_ID` | Cloudflare 帳戶 ID 🆔 | 字串 | `"1a2b3c4d5e6f7g8h9i0j"` | - | 39 | | `CF_API_TOKEN` | Cloudflare API 令牌 🎟️ | 字串 | `"your-cloudflare-api-token"` | - | 40 | | `PROMPT_OPTIMIZATION` | 是否啟用提示詞優化 🌐 | 字符串 | `"true"` 或 `"false"` | - | 41 | | `EXTERNAL_API_BASE` | 外部 API 的基礎 URL 🔗 | 字串 | `"https://api.external-service.com"` | - | 42 | | `EXTERNAL_MODEL` | 外部優化模型名稱 🤖 | 字符串 | `"gpt-3.5-turbo"` | - | 43 | | `EXTERNAL_API_KEY` | 外部 API 的存取密鑰 🗝️ | 字串 | `"your-external-api-key"` | - | 44 | | `FLUX_NUM_STEPS` | Flux 模型的步數 🚶 | 整數 | `"4"` | 4 | 45 | | `IMAGE_EXPIRATION` | 圖像在 KV 中的過期時間(秒)⏳ | 整數 | `"1800"` | 1800 | 46 | 47 | 請確保在 Cloudflare Worker 的環境變數設置中正確配置這些變數。對於有預設值的變數,如不需更改,可保持預設設置。🔧✅ 48 | 49 | > 注意:為了保證安全,請為 `API_KEY` 設置一個複雜的字串。這將用於驗證 API 呼叫的合法性。🔒🛡️ 50 | 51 | ### 創建 KV 命名空間 🗄️📦 52 | 53 | 1. 在 Cloudflare Dashboard 中,轉到 "Workers" 頁面。🖥️ 54 | 2. 點擊 "KV" 選項卡。📑 55 | 3. 創建一個新的命名空間,命名為 "FLUX_CF_KV"。🆕 56 | 4. 在 Worker 的設置中,將這個 KV 命名空間綁定到 `FLUX_CF_KV` 變數。🔗 57 | 58 | ## API 端點和功能 🌐🛠️ 59 | 60 | ### 1. 歡迎頁面 👋 61 | 62 | 訪問 Worker 的根路徑 (`https://..workers.dev/`) 將顯示一個歡迎頁面,確認 API 服務正在運行。✅🏠 63 | 64 | ### 2. 聊天完成端點 💬 65 | 66 | 主要的圖像生成端點: 67 | ``` 68 | https://..workers.dev/v1/chat/completions 69 | ``` 70 | 🎨✨ 71 | 72 | ### 3. 模型資訊端點 ℹ️ 73 | 74 | 獲取可用模型資訊: 75 | ``` 76 | https://..workers.dev/v1/models 77 | ``` 78 | 這個端點返回當前使用的 Flux 模型資訊。🤖📊 79 | 80 | ### 4. 圖像獲取端點 🖼️ 81 | 82 | 獲取生成的圖像: 83 | ``` 84 | https://..workers.dev/image/{image_key} 85 | ``` 86 | 📥🎭 87 | 88 | ## 使用指南 📖🧭 89 | 90 | ### 生成圖像 🖼️🎨 91 | 92 | 發送 POST 請求到聊天完成端點,格式如下: 93 | 94 | ```json 95 | { 96 | "messages": [ 97 | { 98 | "role": "user", 99 | "content": "一隻可愛的貓咪 3:2" 100 | } 101 | ], 102 | "stream": false 103 | } 104 | ``` 105 | 106 | 請求頭必須包含: 107 | 108 | ``` 109 | Authorization: Bearer YOUR_API_KEY 110 | Content-Type: application/json 111 | ``` 112 | 113 | > 重要:請將 `YOUR_API_KEY` 替換為您在環境變數中設置的 `API_KEY` 值。🔑🔄 114 | 115 | ### 串流回應 🌊📡 116 | 117 | 如果您希望接收串流回應,將 `stream` 參數設置為 `true`: 118 | 119 | ```json 120 | { 121 | "messages": [ 122 | { 123 | "role": "user", 124 | "content": "一隻可愛的貓咪 3:2" 125 | } 126 | ], 127 | "stream": true 128 | } 129 | ``` 130 | 131 | 串流回應將以 Server-Sent Events (SSE) 格式返回,允許即時獲取生成進度。⚡🔄 132 | 133 | ### 支援的圖像尺寸 📏🖼️ 134 | 135 | Flux-API-Worker 支援以下預設的圖像尺寸和寬高比: 136 | 137 | - 1:1 (1024x1024) - 預設尺寸 🟦 138 | - 1:2 (512x1024) 📱 139 | - 3:2 (768x512) 🖼️ 140 | - 3:4 (768x1024) 📱 141 | - 16:9 (1024x576) 🖥️ 142 | - 9:16 (576x1024) 📱 143 | 144 | 要指定特定的尺寸,只需在提示詞後面添加相應的比例,例如: 145 | 146 | ``` 147 | "一隻可愛的貓咪 16:9" 148 | ``` 149 | 150 | 如果沒有指定尺寸,系統將預設生成 1:1 (1024x1024) 的圖片。🎛️🔧 151 | 152 | ### 跨源資源共享(CORS)支援 🌍🔓 153 | 154 | Flux-API-Worker 支援 CORS,允許從不同域名的網頁應用程式訪問 API。這意味著您可以在前端 JavaScript 應用中直接呼叫 API,而不會遇到跨域問題。🔗🚫🚧 155 | 156 | ### 在第三方應用中使用 🔗🔌 157 | 158 | Flux-API-Worker 可以輕鬆整合到各種應用中,如 NextWeb、ChatBox 等。在這些應用中配置時: 159 | 160 | 1. 將 API 地址設置為您的 Worker URL(聊天完成端點)。🔗 161 | 2. 輸入您設置的 API KEY。🔑 162 | 3. 無需理會應用提供的 System Message 設置,因為 Flux-API-Worker 使用內置的 System Message。💬🚫 163 | 164 | > 注意:Flux-API-Worker 已經移除了上下文功能,每次呼叫都會生成新的獨特圖像。🆕🖼️ 165 | 166 | ### 回應格式 📤📊 167 | 168 | 非串流回應示例: 169 | 170 | ```json 171 | { 172 | "id": "chatcmpl-1234567890", 173 | "created": 1677649420, 174 | "model": "@cf/black-forest-labs/flux-1-schnell", 175 | "object": "chat.completion", 176 | "choices": [ 177 | { 178 | "index": 0, 179 | "message": { 180 | "role": "assistant", 181 | "content": "🎨 原始提示詞:一隻可愛的貓咪 3:2\n💬 提示詞生成模型:Original Prompt\n🌐 優化後的提示詞:一隻可愛的貓咪\n📐 圖像規格:768x512\n🌟 圖像生成成功!\n以下是結果:\n\n![生成的圖像](https://your-worker-url.workers.dev/image/12345)" 182 | }, 183 | "finish_reason": "stop" 184 | } 185 | ], 186 | "usage": { 187 | "prompt_tokens": 20, 188 | "completion_tokens": 100, 189 | "total_tokens": 120 190 | } 191 | } 192 | ``` 193 | 194 | ## 注意事項 ⚠️🚨 195 | 196 | - 確保所有必要的環境變數都已正確設置。✅🔧 197 | - API 密鑰應當妥善保管,避免在客戶端代碼中暴露。🔒🙈 198 | - 圖像在 KV 儲存中有過期時間(預設 30 分鐘),請及時保存重要的圖像。⏳💾 199 | - 如啟用提示詞優化功能,請確保外部 API 配置正確。🌐🔧 200 | - 使用串流回應時,確保您的客戶端能夠正確處理 Server-Sent Events。🌊📡 201 | 202 | ## 故障排除 🔧🚑 203 | 204 | 1. 遇到未授權錯誤時,請檢查 API 密鑰是否正確設置和使用。🔑❓ 205 | 2. 圖像生成失敗時,請驗證 Cloudflare API Token 是否具有正確的權限。🎟️🔍 206 | 3. 提示詞優化不工作時,請確認 `PROMPT_OPTIMIZATION` 設置為 'true' 且外部 API 配置無誤。🌐🔧 207 | 4. 如果收到 404 錯誤,確保您訪問的是正確的端點路徑。🔍🚷 208 | 5. 對於其他錯誤,檢查 Worker 的日誌以獲取更詳細的錯誤資訊。📋🔬 209 | 210 | ## 進一步定製 🛠️🎨 211 | 212 | 您可以通過修改 Worker 代碼來進一步優化 API 的功能,例如: 213 | 214 | - 調整支援的圖像尺寸和寬高比 📏✂️ 215 | - 修改內置的系統訊息以改變提示詞生成的行為 💬🔄 216 | - 添加額外的錯誤處理和日誌記錄機制 🚨📊 217 | - 實現自定義的速率限制或其他安全措施 🛡️⏱️ 218 | 219 | 我希望這個 README 能協助您快速部署和使用 Flux-API-Worker。如果您有任何疑問或需要進一步的幫助,請隨時與我聯繫。💌👨‍💻👩‍💻 220 | 221 | 如果你覺得這個 repo 幫到了您,請給我一個 star 吧。⭐⭐⭐ 謝謝 222 | -------------------------------------------------------------------------------- /src/flux-api-worker_de.js: -------------------------------------------------------------------------------- 1 | function initConfig(env) { 2 | return { 3 | API_KEY: env.API_KEY, 4 | CF_ACCOUNT_ID: env.CF_ACCOUNT_ID, 5 | CF_API_TOKEN: env.CF_API_TOKEN, 6 | PROMPT_OPTIMIZATION: env.PROMPT_OPTIMIZATION === 'true', 7 | EXTERNAL_API_BASE: env.EXTERNAL_API_BASE, 8 | EXTERNAL_MODEL: env.EXTERNAL_MODEL, 9 | EXTERNAL_API_KEY: env.EXTERNAL_API_KEY, 10 | FLUX_NUM_STEPS: parseInt(env.FLUX_NUM_STEPS, 10) || 4, 11 | FLUX_MODEL: env.FLUX_MODEL || "@cf/black-forest-labs/flux-1-schnell", 12 | IMAGE_EXPIRATION: parseInt(env.IMAGE_EXPIRATION, 10) || 60 * 30, 13 | SYSTEM_MESSAGE: env.SYSTEM_MESSAGE || `You are a prompt generation bot based on the Flux.1 model. Based on the user's requirements, automatically generate drawing prompts that adhere to the Flux.1 format. While you can refer to the provided templates to learn the structure and patterns of the prompts, you must remain flexible to meet various different needs. The final output should be limited to the prompts only, without any additional explanations or information. You must reply to me entirely in English! 14 | 15 | ### **Prompt Generation Logic**: 16 | 17 | 1. **Requirement Analysis**: Extract key information from the user's description, including: 18 | - Characters: Appearance, actions, expressions, etc. 19 | - Scene: Environment, lighting, weather, etc. 20 | - Style: Art style, emotional atmosphere, color scheme, etc. 21 | - **Aspect Ratio**: If the user provides a specific aspect ratio (e.g., "3:2", "16:9"), extract this and integrate it into the final prompt. 22 | - Other elements: Specific objects, background, or effects. 23 | 24 | 2. **Prompt Structure Guidelines**: 25 | - **Concise, precise, and detailed**: Prompts should describe the core subject simply and clearly, with enough detail to generate an image that matches the request. 26 | - **Flexible and varied**: Use the user's description to dynamically create prompts without following rigid templates. Ensure prompts are adapted based on the specific needs of each user, avoiding overly template-based outputs. 27 | - **Descriptions following Flux.1 style**: Prompts must follow the requirements of Flux.1, aiming to include descriptions of the art style, visual effects, and emotional atmosphere. Use keywords and description patterns that match the Flux.1 model's generation process. If a specific aspect ratio is mentioned, ensure it is included in the prompt description. 28 | 29 | 3. **Key Points Summary for Flux.1 Prompts**: 30 | - **Concise and precise subject description**: Clearly identify the subject or scene of the image. 31 | - **Specific description of style and emotional atmosphere**: Ensure the prompt includes information about the art style, lighting, color scheme, and emotional atmosphere of the image. 32 | - **Details on dynamics and action**: Prompts may include important details like actions, emotions, or lighting effects in the scene.` 33 | }; 34 | } 35 | 36 | let CONFIG; 37 | 38 | async function handleRequest(request, env, ctx) { 39 | CONFIG = initConfig(env); 40 | const url = new URL(request.url); 41 | 42 | if (request.method === "GET" && (url.pathname === "/" || url.pathname === "")) { 43 | return new Response(generateWelcomePage(), { 44 | status: 200, 45 | headers: { "Content-Type": "text/html" } 46 | }); 47 | } 48 | 49 | if (url.pathname.startsWith('/image/')) { 50 | return handleImageRequest(request, env, ctx); 51 | } 52 | 53 | if (request.method === "OPTIONS") { 54 | return handleCORS(); 55 | } 56 | 57 | if (!isAuthorized(request)) { 58 | return new Response("Unauthorized", { status: 401 }); 59 | } 60 | 61 | if (url.pathname.endsWith("/v1/models")) { 62 | return handleModelsRequest(); 63 | } 64 | 65 | if (request.method !== "POST" || !url.pathname.endsWith("/v1/chat/completions")) { 66 | return new Response("Not Found", { status: 404 }); 67 | } 68 | 69 | return handleChatCompletions(request, env, ctx); 70 | } 71 | 72 | function generateWelcomePage() { 73 | return ` 74 | 75 | 76 | 77 | 78 | 79 | Flux API - Ready for Requests 80 | 109 | 110 | 111 |
112 |

Welcome to Flux API

113 |

Your Flux API is ready and waiting for requests!

114 |

This API is designed to handle specific endpoints for chat completions and image generation.

115 |

For API documentation and usage instructions, please refer to your provided documentation.

116 |
117 | 118 | 119 | `; 120 | } 121 | 122 | function addCorsHeaders(headers) { 123 | headers.set('Access-Control-Allow-Origin', '*'); 124 | headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); 125 | headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization'); 126 | } 127 | 128 | function handleCORS() { 129 | const headers = new Headers(); 130 | addCorsHeaders(headers); 131 | return new Response(null, { status: 204, headers }); 132 | } 133 | 134 | function isAuthorized(request) { 135 | const authHeader = request.headers.get("Authorization"); 136 | return authHeader && authHeader.startsWith("Bearer ") && authHeader.split(" ")[1] === CONFIG.API_KEY; 137 | } 138 | 139 | function handleModelsRequest() { 140 | const models = [{ id: CONFIG.FLUX_MODEL, object: "model" }]; 141 | const headers = new Headers({ 'Content-Type': 'application/json' }); 142 | addCorsHeaders(headers); 143 | return new Response(JSON.stringify({ data: models, object: "list" }), { headers }); 144 | } 145 | 146 | function parseRatioAndSize(prompt) { 147 | const ratios = { 148 | "1:1": { width: 1024, height: 1024 }, 149 | "1:2": { width: 512, height: 1024 }, 150 | "3:2": { width: 768, height: 512 }, 151 | "3:4": { width: 768, height: 1024 }, 152 | "16:9": { width: 1024, height: 576 }, 153 | "9:16": { width: 576, height: 1024 } 154 | }; 155 | 156 | const ratioMatch = prompt.match(/(\d+:\d+)(?:\s|$)/); 157 | if (ratioMatch) { 158 | const ratio = ratioMatch[1]; 159 | return { 160 | size: ratios[ratio] || ratios["1:1"], 161 | cleanedPrompt: prompt.replace(/\d+:\d+/, "").trim() 162 | }; 163 | } 164 | 165 | return { 166 | size: ratios["1:1"], 167 | cleanedPrompt: prompt.trim() 168 | }; 169 | } 170 | 171 | async function handleChatCompletions(request, env, ctx) { 172 | try { 173 | const data = await request.json(); 174 | let { messages, stream } = data; 175 | 176 | const userMessage = messages.filter(msg => msg.role === "user").pop(); 177 | 178 | if (!userMessage) { 179 | return new Response(JSON.stringify({ error: "未找到用户消息" }), { status: 400, headers: { 'Content-Type': 'application/json' } }); 180 | } 181 | 182 | const originalPrompt = cleanPromptString(userMessage.content); 183 | const model = CONFIG.FLUX_MODEL; 184 | const promptModel = CONFIG.PROMPT_OPTIMIZATION ? CONFIG.EXTERNAL_MODEL : "Original Prompt"; 185 | 186 | const { size: originalSize, cleanedPrompt: cleanedOriginalPrompt } = parseRatioAndSize(originalPrompt); 187 | 188 | const translatedPrompt = await getFluxPrompt(cleanedOriginalPrompt, originalSize); 189 | 190 | const imageUrl = await generateAndStoreFluxImage(model, translatedPrompt, request.url, env, ctx, originalSize); 191 | 192 | const sizeString = `${originalSize.width}x${originalSize.height}`; 193 | 194 | return handleResponse(originalPrompt, translatedPrompt, sizeString, model, imageUrl, promptModel, stream); 195 | } catch (error) { 196 | return new Response(JSON.stringify({ error: "Internal Server Error: " + error.message }), { status: 500, headers: { 'Content-Type': 'application/json' } }); 197 | } 198 | } 199 | 200 | async function getFluxPrompt(prompt, size) { 201 | if (!CONFIG.PROMPT_OPTIMIZATION) { 202 | return prompt; 203 | } 204 | 205 | const aspectRatio = `${size.width}:${size.height}`; 206 | 207 | const requestBody = { 208 | messages: [ 209 | { 210 | role: "system", 211 | content: CONFIG.SYSTEM_MESSAGE 212 | }, 213 | { 214 | role: "user", 215 | content: `Generate a prompt for an image with the aspect ratio ${aspectRatio}. The description is: ${prompt}` 216 | } 217 | ], 218 | model: CONFIG.EXTERNAL_MODEL 219 | }; 220 | 221 | try { 222 | return await getExternalPrompt(requestBody); 223 | } catch (error) { 224 | console.error('Error in getFluxPrompt:', error); 225 | return prompt; 226 | } 227 | } 228 | 229 | async function getExternalPrompt(requestBody) { 230 | const response = await fetch(`${CONFIG.EXTERNAL_API_BASE}/v1/chat/completions`, { 231 | method: 'POST', 232 | headers: { 233 | 'Authorization': `Bearer ${CONFIG.EXTERNAL_API_KEY}`, 234 | 'Content-Type': 'application/json', 235 | 'User-Agent': 'CloudflareWorker/1.0' 236 | }, 237 | body: JSON.stringify(requestBody) 238 | }); 239 | 240 | if (!response.ok) { 241 | throw new Error(`External API request failed with status ${response.status}`); 242 | } 243 | 244 | const jsonResponse = await response.json(); 245 | if (!jsonResponse.choices || jsonResponse.choices.length === 0 || !jsonResponse.choices[0].message) { 246 | throw new Error('Invalid response format from external API'); 247 | } 248 | 249 | return jsonResponse.choices[0].message.content; 250 | } 251 | 252 | async function generateAndStoreFluxImage(model, prompt, requestUrl, env, ctx, size) { 253 | const jsonBody = { 254 | prompt, 255 | num_steps: CONFIG.FLUX_NUM_STEPS, 256 | width: size.width, 257 | height: size.height 258 | }; 259 | const response = await postRequest(model, jsonBody); 260 | const jsonResponse = await response.json(); 261 | const base64ImageData = jsonResponse.result.image; 262 | 263 | const imageBuffer = base64ToArrayBuffer(base64ImageData); 264 | 265 | const key = `image_${Date.now()}_${Math.random().toString(36).substring(7)}`; 266 | 267 | await env.FLUX_CF_KV.put(key, imageBuffer, { 268 | expirationTtl: CONFIG.IMAGE_EXPIRATION, 269 | metadata: { contentType: 'image/png' } 270 | }); 271 | 272 | return `${new URL(requestUrl).origin}/image/${key}`; 273 | } 274 | 275 | function generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel) { 276 | return `🎨 Ursprünglicher Prompt: ${originalPrompt}\n` + 277 | `💬 Prompt-Modell: ${promptModel}\n` + 278 | `🌐 Übersetzter Prompt: ${translatedPrompt}\n` + 279 | `📐 Bildgröße: ${size}\n` + 280 | `🌟 Bild erfolgreich generiert!\n` + 281 | `Hier ist das Ergebnis:\n\n` + 282 | `![Generiertes Bild](${imageUrl})`; 283 | } 284 | 285 | function handleResponse(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel, isStream) { 286 | const content = generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel); 287 | const commonFields = { 288 | id: `chatcmpl-${Date.now()}`, 289 | created: Math.floor(Date.now() / 1000), 290 | model: model 291 | }; 292 | 293 | if (isStream) { 294 | const encoder = new TextEncoder(); 295 | const stream = new ReadableStream({ 296 | start(controller) { 297 | controller.enqueue(encoder.encode(`data: ${JSON.stringify({ 298 | ...commonFields, 299 | object: "chat.completion.chunk", 300 | choices: [{ delta: { content: content }, index: 0, finish_reason: null }] 301 | })}\n\n`)); 302 | controller.enqueue(encoder.encode('data: [DONE]\n\n')); 303 | controller.close(); 304 | } 305 | }); 306 | 307 | const headers = new Headers({ 308 | "Content-Type": "text/event-stream", 309 | "Cache-Control": "no-cache", 310 | "Connection": "keep-alive" 311 | }); 312 | addCorsHeaders(headers); 313 | return new Response(stream, { headers }); 314 | } else { 315 | const response = { 316 | ...commonFields, 317 | object: "chat.completion", 318 | choices: [{ 319 | index: 0, 320 | message: { role: "assistant", content }, 321 | finish_reason: "stop" 322 | }], 323 | usage: { 324 | prompt_tokens: translatedPrompt.length, 325 | completion_tokens: content.length, 326 | total_tokens: translatedPrompt.length + content.length 327 | } 328 | }; 329 | 330 | const headers = new Headers({ 'Content-Type': 'application/json' }); 331 | addCorsHeaders(headers); 332 | return new Response(JSON.stringify(response), { headers }); 333 | } 334 | } 335 | 336 | async function handleImageRequest(request, env, ctx) { 337 | const url = new URL(request.url); 338 | const key = url.pathname.split('/').pop(); 339 | 340 | const imageData = await env.FLUX_CF_KV.get(key, 'arrayBuffer'); 341 | if (!imageData) { 342 | return new Response('Image not found', { status: 404 }); 343 | } 344 | 345 | const headers = new Headers({ 346 | 'Content-Type': 'image/png', 347 | 'Cache-Control': 'public, max-age=604800', 348 | }); 349 | addCorsHeaders(headers); 350 | return new Response(imageData, { headers }); 351 | } 352 | 353 | function base64ToArrayBuffer(base64) { 354 | const binaryString = atob(base64); 355 | const bytes = new Uint8Array(binaryString.length); 356 | for (let i = 0; i < binaryString.length; i++) { 357 | bytes[i] = binaryString.charCodeAt(i); 358 | } 359 | return bytes.buffer; 360 | } 361 | 362 | async function postRequest(model, jsonBody) { 363 | const apiUrl = `https://api.cloudflare.com/client/v4/accounts/${CONFIG.CF_ACCOUNT_ID}/ai/run/${model}`; 364 | const response = await fetch(apiUrl, { 365 | method: 'POST', 366 | headers: { 367 | 'Authorization': `Bearer ${CONFIG.CF_API_TOKEN}`, 368 | 'Content-Type': 'application/json' 369 | }, 370 | body: JSON.stringify(jsonBody) 371 | }); 372 | 373 | if (!response.ok) { 374 | throw new Error('Cloudflare API request failed: ' + response.status); 375 | } 376 | return response; 377 | } 378 | 379 | function cleanPromptString(prompt) { 380 | return prompt.replace(/---n?tl/, "").trim(); 381 | } 382 | 383 | export default { 384 | async fetch(request, env, ctx) { 385 | CONFIG = initConfig(env); 386 | return handleRequest(request, env, ctx); 387 | } 388 | }; 389 | -------------------------------------------------------------------------------- /src/flux-api-worker_en.js: -------------------------------------------------------------------------------- 1 | function initConfig(env) { 2 | return { 3 | API_KEY: env.API_KEY, 4 | CF_ACCOUNT_ID: env.CF_ACCOUNT_ID, 5 | CF_API_TOKEN: env.CF_API_TOKEN, 6 | PROMPT_OPTIMIZATION: env.PROMPT_OPTIMIZATION === 'true', 7 | EXTERNAL_API_BASE: env.EXTERNAL_API_BASE, 8 | EXTERNAL_MODEL: env.EXTERNAL_MODEL, 9 | EXTERNAL_API_KEY: env.EXTERNAL_API_KEY, 10 | FLUX_NUM_STEPS: parseInt(env.FLUX_NUM_STEPS, 10) || 4, 11 | FLUX_MODEL: env.FLUX_MODEL || "@cf/black-forest-labs/flux-1-schnell", 12 | IMAGE_EXPIRATION: parseInt(env.IMAGE_EXPIRATION, 10) || 60 * 30, 13 | SYSTEM_MESSAGE: env.SYSTEM_MESSAGE || `You are a prompt generation bot based on the Flux.1 model. Based on the user's requirements, automatically generate drawing prompts that adhere to the Flux.1 format. While you can refer to the provided templates to learn the structure and patterns of the prompts, you must remain flexible to meet various different needs. The final output should be limited to the prompts only, without any additional explanations or information. You must reply to me entirely in English! 14 | 15 | ### **Prompt Generation Logic**: 16 | 17 | 1. **Requirement Analysis**: Extract key information from the user's description, including: 18 | - Characters: Appearance, actions, expressions, etc. 19 | - Scene: Environment, lighting, weather, etc. 20 | - Style: Art style, emotional atmosphere, color scheme, etc. 21 | - **Aspect Ratio**: If the user provides a specific aspect ratio (e.g., "3:2", "16:9"), extract this and integrate it into the final prompt. 22 | - Other elements: Specific objects, background, or effects. 23 | 24 | 2. **Prompt Structure Guidelines**: 25 | - **Concise, precise, and detailed**: Prompts should describe the core subject simply and clearly, with enough detail to generate an image that matches the request. 26 | - **Flexible and varied**: Use the user's description to dynamically create prompts without following rigid templates. Ensure prompts are adapted based on the specific needs of each user, avoiding overly template-based outputs. 27 | - **Descriptions following Flux.1 style**: Prompts must follow the requirements of Flux.1, aiming to include descriptions of the art style, visual effects, and emotional atmosphere. Use keywords and description patterns that match the Flux.1 model's generation process. If a specific aspect ratio is mentioned, ensure it is included in the prompt description. 28 | 29 | 3. **Key Points Summary for Flux.1 Prompts**: 30 | - **Concise and precise subject description**: Clearly identify the subject or scene of the image. 31 | - **Specific description of style and emotional atmosphere**: Ensure the prompt includes information about the art style, lighting, color scheme, and emotional atmosphere of the image. 32 | - **Details on dynamics and action**: Prompts may include important details like actions, emotions, or lighting effects in the scene.` 33 | }; 34 | } 35 | 36 | let CONFIG; 37 | 38 | async function handleRequest(request, env, ctx) { 39 | CONFIG = initConfig(env); 40 | const url = new URL(request.url); 41 | 42 | if (request.method === "GET" && (url.pathname === "/" || url.pathname === "")) { 43 | return new Response(generateWelcomePage(), { 44 | status: 200, 45 | headers: { "Content-Type": "text/html" } 46 | }); 47 | } 48 | 49 | if (url.pathname.startsWith('/image/')) { 50 | return handleImageRequest(request, env, ctx); 51 | } 52 | 53 | if (request.method === "OPTIONS") { 54 | return handleCORS(); 55 | } 56 | 57 | if (!isAuthorized(request)) { 58 | return new Response("Unauthorized", { status: 401 }); 59 | } 60 | 61 | if (url.pathname.endsWith("/v1/models")) { 62 | return handleModelsRequest(); 63 | } 64 | 65 | if (request.method !== "POST" || !url.pathname.endsWith("/v1/chat/completions")) { 66 | return new Response("Not Found", { status: 404 }); 67 | } 68 | 69 | return handleChatCompletions(request, env, ctx); 70 | } 71 | 72 | function generateWelcomePage() { 73 | return ` 74 | 75 | 76 | 77 | 78 | 79 | Flux API - Ready for Requests 80 | 109 | 110 | 111 |
112 |

Welcome to Flux API

113 |

Your Flux API is ready and waiting for requests!

114 |

This API is designed to handle specific endpoints for chat completions and image generation.

115 |

For API documentation and usage instructions, please refer to your provided documentation.

116 |
117 | 118 | 119 | `; 120 | } 121 | 122 | function addCorsHeaders(headers) { 123 | headers.set('Access-Control-Allow-Origin', '*'); 124 | headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); 125 | headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization'); 126 | } 127 | 128 | function handleCORS() { 129 | const headers = new Headers(); 130 | addCorsHeaders(headers); 131 | return new Response(null, { status: 204, headers }); 132 | } 133 | 134 | function isAuthorized(request) { 135 | const authHeader = request.headers.get("Authorization"); 136 | return authHeader && authHeader.startsWith("Bearer ") && authHeader.split(" ")[1] === CONFIG.API_KEY; 137 | } 138 | 139 | function handleModelsRequest() { 140 | const models = [{ id: CONFIG.FLUX_MODEL, object: "model" }]; 141 | const headers = new Headers({ 'Content-Type': 'application/json' }); 142 | addCorsHeaders(headers); 143 | return new Response(JSON.stringify({ data: models, object: "list" }), { headers }); 144 | } 145 | 146 | function parseRatioAndSize(prompt) { 147 | const ratios = { 148 | "1:1": { width: 1024, height: 1024 }, 149 | "1:2": { width: 512, height: 1024 }, 150 | "3:2": { width: 768, height: 512 }, 151 | "3:4": { width: 768, height: 1024 }, 152 | "16:9": { width: 1024, height: 576 }, 153 | "9:16": { width: 576, height: 1024 } 154 | }; 155 | 156 | const ratioMatch = prompt.match(/(\d+:\d+)(?:\s|$)/); 157 | if (ratioMatch) { 158 | const ratio = ratioMatch[1]; 159 | return { 160 | size: ratios[ratio] || ratios["1:1"], 161 | cleanedPrompt: prompt.replace(/\d+:\d+/, "").trim() 162 | }; 163 | } 164 | 165 | return { 166 | size: ratios["1:1"], 167 | cleanedPrompt: prompt.trim() 168 | }; 169 | } 170 | 171 | async function handleChatCompletions(request, env, ctx) { 172 | try { 173 | const data = await request.json(); 174 | let { messages, stream } = data; 175 | 176 | const userMessage = messages.filter(msg => msg.role === "user").pop(); 177 | 178 | if (!userMessage) { 179 | return new Response(JSON.stringify({ error: "未找到用户消息" }), { status: 400, headers: { 'Content-Type': 'application/json' } }); 180 | } 181 | 182 | const originalPrompt = cleanPromptString(userMessage.content); 183 | const model = CONFIG.FLUX_MODEL; 184 | const promptModel = CONFIG.PROMPT_OPTIMIZATION ? CONFIG.EXTERNAL_MODEL : "Original Prompt"; 185 | 186 | const { size: originalSize, cleanedPrompt: cleanedOriginalPrompt } = parseRatioAndSize(originalPrompt); 187 | 188 | const translatedPrompt = await getFluxPrompt(cleanedOriginalPrompt, originalSize); 189 | 190 | const imageUrl = await generateAndStoreFluxImage(model, translatedPrompt, request.url, env, ctx, originalSize); 191 | 192 | const sizeString = `${originalSize.width}x${originalSize.height}`; 193 | 194 | return handleResponse(originalPrompt, translatedPrompt, sizeString, model, imageUrl, promptModel, stream); 195 | } catch (error) { 196 | return new Response(JSON.stringify({ error: "Internal Server Error: " + error.message }), { status: 500, headers: { 'Content-Type': 'application/json' } }); 197 | } 198 | } 199 | 200 | async function getFluxPrompt(prompt, size) { 201 | if (!CONFIG.PROMPT_OPTIMIZATION) { 202 | return prompt; 203 | } 204 | 205 | const aspectRatio = `${size.width}:${size.height}`; 206 | 207 | const requestBody = { 208 | messages: [ 209 | { 210 | role: "system", 211 | content: CONFIG.SYSTEM_MESSAGE 212 | }, 213 | { 214 | role: "user", 215 | content: `Generate a prompt for an image with the aspect ratio ${aspectRatio}. The description is: ${prompt}` 216 | } 217 | ], 218 | model: CONFIG.EXTERNAL_MODEL 219 | }; 220 | 221 | try { 222 | return await getExternalPrompt(requestBody); 223 | } catch (error) { 224 | console.error('Error in getFluxPrompt:', error); 225 | return prompt; 226 | } 227 | } 228 | 229 | async function getExternalPrompt(requestBody) { 230 | const response = await fetch(`${CONFIG.EXTERNAL_API_BASE}/v1/chat/completions`, { 231 | method: 'POST', 232 | headers: { 233 | 'Authorization': `Bearer ${CONFIG.EXTERNAL_API_KEY}`, 234 | 'Content-Type': 'application/json', 235 | 'User-Agent': 'CloudflareWorker/1.0' 236 | }, 237 | body: JSON.stringify(requestBody) 238 | }); 239 | 240 | if (!response.ok) { 241 | throw new Error(`External API request failed with status ${response.status}`); 242 | } 243 | 244 | const jsonResponse = await response.json(); 245 | if (!jsonResponse.choices || jsonResponse.choices.length === 0 || !jsonResponse.choices[0].message) { 246 | throw new Error('Invalid response format from external API'); 247 | } 248 | 249 | return jsonResponse.choices[0].message.content; 250 | } 251 | 252 | async function generateAndStoreFluxImage(model, prompt, requestUrl, env, ctx, size) { 253 | const jsonBody = { 254 | prompt, 255 | num_steps: CONFIG.FLUX_NUM_STEPS, 256 | width: size.width, 257 | height: size.height 258 | }; 259 | const response = await postRequest(model, jsonBody); 260 | const jsonResponse = await response.json(); 261 | const base64ImageData = jsonResponse.result.image; 262 | 263 | const imageBuffer = base64ToArrayBuffer(base64ImageData); 264 | 265 | const key = `image_${Date.now()}_${Math.random().toString(36).substring(7)}`; 266 | 267 | await env.FLUX_CF_KV.put(key, imageBuffer, { 268 | expirationTtl: CONFIG.IMAGE_EXPIRATION, 269 | metadata: { contentType: 'image/png' } 270 | }); 271 | 272 | return `${new URL(requestUrl).origin}/image/${key}`; 273 | } 274 | 275 | function generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel) { 276 | return `🎨 Original Prompt: ${originalPrompt}\n` + 277 | `💬 Prompt Model: ${promptModel}\n` + 278 | `🌐 Translated Prompt: ${translatedPrompt}\n` + 279 | `📐 Image Size: ${size}\n` + 280 | `🌟 Image Successfully Generated!\n` + 281 | `Here is the result:\n\n` + 282 | `![Generated Image](${imageUrl})`; 283 | } 284 | 285 | function handleResponse(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel, isStream) { 286 | const content = generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel); 287 | const commonFields = { 288 | id: `chatcmpl-${Date.now()}`, 289 | created: Math.floor(Date.now() / 1000), 290 | model: model 291 | }; 292 | 293 | if (isStream) { 294 | const encoder = new TextEncoder(); 295 | const stream = new ReadableStream({ 296 | start(controller) { 297 | controller.enqueue(encoder.encode(`data: ${JSON.stringify({ 298 | ...commonFields, 299 | object: "chat.completion.chunk", 300 | choices: [{ delta: { content: content }, index: 0, finish_reason: null }] 301 | })}\n\n`)); 302 | controller.enqueue(encoder.encode('data: [DONE]\n\n')); 303 | controller.close(); 304 | } 305 | }); 306 | 307 | const headers = new Headers({ 308 | "Content-Type": "text/event-stream", 309 | "Cache-Control": "no-cache", 310 | "Connection": "keep-alive" 311 | }); 312 | addCorsHeaders(headers); 313 | return new Response(stream, { headers }); 314 | } else { 315 | const response = { 316 | ...commonFields, 317 | object: "chat.completion", 318 | choices: [{ 319 | index: 0, 320 | message: { role: "assistant", content }, 321 | finish_reason: "stop" 322 | }], 323 | usage: { 324 | prompt_tokens: translatedPrompt.length, 325 | completion_tokens: content.length, 326 | total_tokens: translatedPrompt.length + content.length 327 | } 328 | }; 329 | 330 | const headers = new Headers({ 'Content-Type': 'application/json' }); 331 | addCorsHeaders(headers); 332 | return new Response(JSON.stringify(response), { headers }); 333 | } 334 | } 335 | 336 | async function handleImageRequest(request, env, ctx) { 337 | const url = new URL(request.url); 338 | const key = url.pathname.split('/').pop(); 339 | 340 | const imageData = await env.FLUX_CF_KV.get(key, 'arrayBuffer'); 341 | if (!imageData) { 342 | return new Response('Image not found', { status: 404 }); 343 | } 344 | 345 | const headers = new Headers({ 346 | 'Content-Type': 'image/png', 347 | 'Cache-Control': 'public, max-age=604800', 348 | }); 349 | addCorsHeaders(headers); 350 | return new Response(imageData, { headers }); 351 | } 352 | 353 | function base64ToArrayBuffer(base64) { 354 | const binaryString = atob(base64); 355 | const bytes = new Uint8Array(binaryString.length); 356 | for (let i = 0; i < binaryString.length; i++) { 357 | bytes[i] = binaryString.charCodeAt(i); 358 | } 359 | return bytes.buffer; 360 | } 361 | 362 | async function postRequest(model, jsonBody) { 363 | const apiUrl = `https://api.cloudflare.com/client/v4/accounts/${CONFIG.CF_ACCOUNT_ID}/ai/run/${model}`; 364 | const response = await fetch(apiUrl, { 365 | method: 'POST', 366 | headers: { 367 | 'Authorization': `Bearer ${CONFIG.CF_API_TOKEN}`, 368 | 'Content-Type': 'application/json' 369 | }, 370 | body: JSON.stringify(jsonBody) 371 | }); 372 | 373 | if (!response.ok) { 374 | throw new Error('Cloudflare API request failed: ' + response.status); 375 | } 376 | return response; 377 | } 378 | 379 | function cleanPromptString(prompt) { 380 | return prompt.replace(/---n?tl/, "").trim(); 381 | } 382 | 383 | export default { 384 | async fetch(request, env, ctx) { 385 | CONFIG = initConfig(env); 386 | return handleRequest(request, env, ctx); 387 | } 388 | }; 389 | -------------------------------------------------------------------------------- /src/flux-api-worker_es.js: -------------------------------------------------------------------------------- 1 | function initConfig(env) { 2 | return { 3 | API_KEY: env.API_KEY, 4 | CF_ACCOUNT_ID: env.CF_ACCOUNT_ID, 5 | CF_API_TOKEN: env.CF_API_TOKEN, 6 | PROMPT_OPTIMIZATION: env.PROMPT_OPTIMIZATION === 'true', 7 | EXTERNAL_API_BASE: env.EXTERNAL_API_BASE, 8 | EXTERNAL_MODEL: env.EXTERNAL_MODEL, 9 | EXTERNAL_API_KEY: env.EXTERNAL_API_KEY, 10 | FLUX_NUM_STEPS: parseInt(env.FLUX_NUM_STEPS, 10) || 4, 11 | FLUX_MODEL: env.FLUX_MODEL || "@cf/black-forest-labs/flux-1-schnell", 12 | IMAGE_EXPIRATION: parseInt(env.IMAGE_EXPIRATION, 10) || 60 * 30, 13 | SYSTEM_MESSAGE: env.SYSTEM_MESSAGE || `You are a prompt generation bot based on the Flux.1 model. Based on the user's requirements, automatically generate drawing prompts that adhere to the Flux.1 format. While you can refer to the provided templates to learn the structure and patterns of the prompts, you must remain flexible to meet various different needs. The final output should be limited to the prompts only, without any additional explanations or information. You must reply to me entirely in English! 14 | 15 | ### **Prompt Generation Logic**: 16 | 17 | 1. **Requirement Analysis**: Extract key information from the user's description, including: 18 | - Characters: Appearance, actions, expressions, etc. 19 | - Scene: Environment, lighting, weather, etc. 20 | - Style: Art style, emotional atmosphere, color scheme, etc. 21 | - **Aspect Ratio**: If the user provides a specific aspect ratio (e.g., "3:2", "16:9"), extract this and integrate it into the final prompt. 22 | - Other elements: Specific objects, background, or effects. 23 | 24 | 2. **Prompt Structure Guidelines**: 25 | - **Concise, precise, and detailed**: Prompts should describe the core subject simply and clearly, with enough detail to generate an image that matches the request. 26 | - **Flexible and varied**: Use the user's description to dynamically create prompts without following rigid templates. Ensure prompts are adapted based on the specific needs of each user, avoiding overly template-based outputs. 27 | - **Descriptions following Flux.1 style**: Prompts must follow the requirements of Flux.1, aiming to include descriptions of the art style, visual effects, and emotional atmosphere. Use keywords and description patterns that match the Flux.1 model's generation process. If a specific aspect ratio is mentioned, ensure it is included in the prompt description. 28 | 29 | 3. **Key Points Summary for Flux.1 Prompts**: 30 | - **Concise and precise subject description**: Clearly identify the subject or scene of the image. 31 | - **Specific description of style and emotional atmosphere**: Ensure the prompt includes information about the art style, lighting, color scheme, and emotional atmosphere of the image. 32 | - **Details on dynamics and action**: Prompts may include important details like actions, emotions, or lighting effects in the scene.` 33 | }; 34 | } 35 | 36 | let CONFIG; 37 | 38 | async function handleRequest(request, env, ctx) { 39 | CONFIG = initConfig(env); 40 | const url = new URL(request.url); 41 | 42 | if (request.method === "GET" && (url.pathname === "/" || url.pathname === "")) { 43 | return new Response(generateWelcomePage(), { 44 | status: 200, 45 | headers: { "Content-Type": "text/html" } 46 | }); 47 | } 48 | 49 | if (url.pathname.startsWith('/image/')) { 50 | return handleImageRequest(request, env, ctx); 51 | } 52 | 53 | if (request.method === "OPTIONS") { 54 | return handleCORS(); 55 | } 56 | 57 | if (!isAuthorized(request)) { 58 | return new Response("Unauthorized", { status: 401 }); 59 | } 60 | 61 | if (url.pathname.endsWith("/v1/models")) { 62 | return handleModelsRequest(); 63 | } 64 | 65 | if (request.method !== "POST" || !url.pathname.endsWith("/v1/chat/completions")) { 66 | return new Response("Not Found", { status: 404 }); 67 | } 68 | 69 | return handleChatCompletions(request, env, ctx); 70 | } 71 | 72 | function generateWelcomePage() { 73 | return ` 74 | 75 | 76 | 77 | 78 | 79 | Flux API - Ready for Requests 80 | 109 | 110 | 111 |
112 |

Welcome to Flux API

113 |

Your Flux API is ready and waiting for requests!

114 |

This API is designed to handle specific endpoints for chat completions and image generation.

115 |

For API documentation and usage instructions, please refer to your provided documentation.

116 |
117 | 118 | 119 | `; 120 | } 121 | 122 | function addCorsHeaders(headers) { 123 | headers.set('Access-Control-Allow-Origin', '*'); 124 | headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); 125 | headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization'); 126 | } 127 | 128 | function handleCORS() { 129 | const headers = new Headers(); 130 | addCorsHeaders(headers); 131 | return new Response(null, { status: 204, headers }); 132 | } 133 | 134 | function isAuthorized(request) { 135 | const authHeader = request.headers.get("Authorization"); 136 | return authHeader && authHeader.startsWith("Bearer ") && authHeader.split(" ")[1] === CONFIG.API_KEY; 137 | } 138 | 139 | function handleModelsRequest() { 140 | const models = [{ id: CONFIG.FLUX_MODEL, object: "model" }]; 141 | const headers = new Headers({ 'Content-Type': 'application/json' }); 142 | addCorsHeaders(headers); 143 | return new Response(JSON.stringify({ data: models, object: "list" }), { headers }); 144 | } 145 | 146 | function parseRatioAndSize(prompt) { 147 | const ratios = { 148 | "1:1": { width: 1024, height: 1024 }, 149 | "1:2": { width: 512, height: 1024 }, 150 | "3:2": { width: 768, height: 512 }, 151 | "3:4": { width: 768, height: 1024 }, 152 | "16:9": { width: 1024, height: 576 }, 153 | "9:16": { width: 576, height: 1024 } 154 | }; 155 | 156 | const ratioMatch = prompt.match(/(\d+:\d+)(?:\s|$)/); 157 | if (ratioMatch) { 158 | const ratio = ratioMatch[1]; 159 | return { 160 | size: ratios[ratio] || ratios["1:1"], 161 | cleanedPrompt: prompt.replace(/\d+:\d+/, "").trim() 162 | }; 163 | } 164 | 165 | return { 166 | size: ratios["1:1"], 167 | cleanedPrompt: prompt.trim() 168 | }; 169 | } 170 | 171 | async function handleChatCompletions(request, env, ctx) { 172 | try { 173 | const data = await request.json(); 174 | let { messages, stream } = data; 175 | 176 | const userMessage = messages.filter(msg => msg.role === "user").pop(); 177 | 178 | if (!userMessage) { 179 | return new Response(JSON.stringify({ error: "未找到用户消息" }), { status: 400, headers: { 'Content-Type': 'application/json' } }); 180 | } 181 | 182 | const originalPrompt = cleanPromptString(userMessage.content); 183 | const model = CONFIG.FLUX_MODEL; 184 | const promptModel = CONFIG.PROMPT_OPTIMIZATION ? CONFIG.EXTERNAL_MODEL : "Original Prompt"; 185 | 186 | const { size: originalSize, cleanedPrompt: cleanedOriginalPrompt } = parseRatioAndSize(originalPrompt); 187 | 188 | const translatedPrompt = await getFluxPrompt(cleanedOriginalPrompt, originalSize); 189 | 190 | const imageUrl = await generateAndStoreFluxImage(model, translatedPrompt, request.url, env, ctx, originalSize); 191 | 192 | const sizeString = `${originalSize.width}x${originalSize.height}`; 193 | 194 | return handleResponse(originalPrompt, translatedPrompt, sizeString, model, imageUrl, promptModel, stream); 195 | } catch (error) { 196 | return new Response(JSON.stringify({ error: "Internal Server Error: " + error.message }), { status: 500, headers: { 'Content-Type': 'application/json' } }); 197 | } 198 | } 199 | 200 | async function getFluxPrompt(prompt, size) { 201 | if (!CONFIG.PROMPT_OPTIMIZATION) { 202 | return prompt; 203 | } 204 | 205 | const aspectRatio = `${size.width}:${size.height}`; 206 | 207 | const requestBody = { 208 | messages: [ 209 | { 210 | role: "system", 211 | content: CONFIG.SYSTEM_MESSAGE 212 | }, 213 | { 214 | role: "user", 215 | content: `Generate a prompt for an image with the aspect ratio ${aspectRatio}. The description is: ${prompt}` 216 | } 217 | ], 218 | model: CONFIG.EXTERNAL_MODEL 219 | }; 220 | 221 | try { 222 | return await getExternalPrompt(requestBody); 223 | } catch (error) { 224 | console.error('Error in getFluxPrompt:', error); 225 | return prompt; 226 | } 227 | } 228 | 229 | async function getExternalPrompt(requestBody) { 230 | const response = await fetch(`${CONFIG.EXTERNAL_API_BASE}/v1/chat/completions`, { 231 | method: 'POST', 232 | headers: { 233 | 'Authorization': `Bearer ${CONFIG.EXTERNAL_API_KEY}`, 234 | 'Content-Type': 'application/json', 235 | 'User-Agent': 'CloudflareWorker/1.0' 236 | }, 237 | body: JSON.stringify(requestBody) 238 | }); 239 | 240 | if (!response.ok) { 241 | throw new Error(`External API request failed with status ${response.status}`); 242 | } 243 | 244 | const jsonResponse = await response.json(); 245 | if (!jsonResponse.choices || jsonResponse.choices.length === 0 || !jsonResponse.choices[0].message) { 246 | throw new Error('Invalid response format from external API'); 247 | } 248 | 249 | return jsonResponse.choices[0].message.content; 250 | } 251 | 252 | async function generateAndStoreFluxImage(model, prompt, requestUrl, env, ctx, size) { 253 | const jsonBody = { 254 | prompt, 255 | num_steps: CONFIG.FLUX_NUM_STEPS, 256 | width: size.width, 257 | height: size.height 258 | }; 259 | const response = await postRequest(model, jsonBody); 260 | const jsonResponse = await response.json(); 261 | const base64ImageData = jsonResponse.result.image; 262 | 263 | const imageBuffer = base64ToArrayBuffer(base64ImageData); 264 | 265 | const key = `image_${Date.now()}_${Math.random().toString(36).substring(7)}`; 266 | 267 | await env.FLUX_CF_KV.put(key, imageBuffer, { 268 | expirationTtl: CONFIG.IMAGE_EXPIRATION, 269 | metadata: { contentType: 'image/png' } 270 | }); 271 | 272 | return `${new URL(requestUrl).origin}/image/${key}`; 273 | } 274 | 275 | function generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel) { 276 | return `🎨 Prompt Original: ${originalPrompt}\n` + 277 | `💬 Modelo de Prompt: ${promptModel}\n` + 278 | `🌐 Prompt Traducido: ${translatedPrompt}\n` + 279 | `📐 Tamaño de Imagen: ${size}\n` + 280 | `🌟 ¡Imagen Generada con Éxito!\n` + 281 | `Aquí está el resultado:\n\n` + 282 | `![Imagen Generada](${imageUrl})`; 283 | } 284 | 285 | function handleResponse(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel, isStream) { 286 | const content = generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel); 287 | const commonFields = { 288 | id: `chatcmpl-${Date.now()}`, 289 | created: Math.floor(Date.now() / 1000), 290 | model: model 291 | }; 292 | 293 | if (isStream) { 294 | const encoder = new TextEncoder(); 295 | const stream = new ReadableStream({ 296 | start(controller) { 297 | controller.enqueue(encoder.encode(`data: ${JSON.stringify({ 298 | ...commonFields, 299 | object: "chat.completion.chunk", 300 | choices: [{ delta: { content: content }, index: 0, finish_reason: null }] 301 | })}\n\n`)); 302 | controller.enqueue(encoder.encode('data: [DONE]\n\n')); 303 | controller.close(); 304 | } 305 | }); 306 | 307 | const headers = new Headers({ 308 | "Content-Type": "text/event-stream", 309 | "Cache-Control": "no-cache", 310 | "Connection": "keep-alive" 311 | }); 312 | addCorsHeaders(headers); 313 | return new Response(stream, { headers }); 314 | } else { 315 | const response = { 316 | ...commonFields, 317 | object: "chat.completion", 318 | choices: [{ 319 | index: 0, 320 | message: { role: "assistant", content }, 321 | finish_reason: "stop" 322 | }], 323 | usage: { 324 | prompt_tokens: translatedPrompt.length, 325 | completion_tokens: content.length, 326 | total_tokens: translatedPrompt.length + content.length 327 | } 328 | }; 329 | 330 | const headers = new Headers({ 'Content-Type': 'application/json' }); 331 | addCorsHeaders(headers); 332 | return new Response(JSON.stringify(response), { headers }); 333 | } 334 | } 335 | 336 | async function handleImageRequest(request, env, ctx) { 337 | const url = new URL(request.url); 338 | const key = url.pathname.split('/').pop(); 339 | 340 | const imageData = await env.FLUX_CF_KV.get(key, 'arrayBuffer'); 341 | if (!imageData) { 342 | return new Response('Image not found', { status: 404 }); 343 | } 344 | 345 | const headers = new Headers({ 346 | 'Content-Type': 'image/png', 347 | 'Cache-Control': 'public, max-age=604800', 348 | }); 349 | addCorsHeaders(headers); 350 | return new Response(imageData, { headers }); 351 | } 352 | 353 | function base64ToArrayBuffer(base64) { 354 | const binaryString = atob(base64); 355 | const bytes = new Uint8Array(binaryString.length); 356 | for (let i = 0; i < binaryString.length; i++) { 357 | bytes[i] = binaryString.charCodeAt(i); 358 | } 359 | return bytes.buffer; 360 | } 361 | 362 | async function postRequest(model, jsonBody) { 363 | const apiUrl = `https://api.cloudflare.com/client/v4/accounts/${CONFIG.CF_ACCOUNT_ID}/ai/run/${model}`; 364 | const response = await fetch(apiUrl, { 365 | method: 'POST', 366 | headers: { 367 | 'Authorization': `Bearer ${CONFIG.CF_API_TOKEN}`, 368 | 'Content-Type': 'application/json' 369 | }, 370 | body: JSON.stringify(jsonBody) 371 | }); 372 | 373 | if (!response.ok) { 374 | throw new Error('Cloudflare API request failed: ' + response.status); 375 | } 376 | return response; 377 | } 378 | 379 | function cleanPromptString(prompt) { 380 | return prompt.replace(/---n?tl/, "").trim(); 381 | } 382 | 383 | export default { 384 | async fetch(request, env, ctx) { 385 | CONFIG = initConfig(env); 386 | return handleRequest(request, env, ctx); 387 | } 388 | }; 389 | -------------------------------------------------------------------------------- /src/flux-api-worker_fr.js: -------------------------------------------------------------------------------- 1 | function initConfig(env) { 2 | return { 3 | API_KEY: env.API_KEY, 4 | CF_ACCOUNT_ID: env.CF_ACCOUNT_ID, 5 | CF_API_TOKEN: env.CF_API_TOKEN, 6 | PROMPT_OPTIMIZATION: env.PROMPT_OPTIMIZATION === 'true', 7 | EXTERNAL_API_BASE: env.EXTERNAL_API_BASE, 8 | EXTERNAL_MODEL: env.EXTERNAL_MODEL, 9 | EXTERNAL_API_KEY: env.EXTERNAL_API_KEY, 10 | FLUX_NUM_STEPS: parseInt(env.FLUX_NUM_STEPS, 10) || 4, 11 | FLUX_MODEL: env.FLUX_MODEL || "@cf/black-forest-labs/flux-1-schnell", 12 | IMAGE_EXPIRATION: parseInt(env.IMAGE_EXPIRATION, 10) || 60 * 30, 13 | SYSTEM_MESSAGE: env.SYSTEM_MESSAGE || `You are a prompt generation bot based on the Flux.1 model. Based on the user's requirements, automatically generate drawing prompts that adhere to the Flux.1 format. While you can refer to the provided templates to learn the structure and patterns of the prompts, you must remain flexible to meet various different needs. The final output should be limited to the prompts only, without any additional explanations or information. You must reply to me entirely in English! 14 | 15 | ### **Prompt Generation Logic**: 16 | 17 | 1. **Requirement Analysis**: Extract key information from the user's description, including: 18 | - Characters: Appearance, actions, expressions, etc. 19 | - Scene: Environment, lighting, weather, etc. 20 | - Style: Art style, emotional atmosphere, color scheme, etc. 21 | - **Aspect Ratio**: If the user provides a specific aspect ratio (e.g., "3:2", "16:9"), extract this and integrate it into the final prompt. 22 | - Other elements: Specific objects, background, or effects. 23 | 24 | 2. **Prompt Structure Guidelines**: 25 | - **Concise, precise, and detailed**: Prompts should describe the core subject simply and clearly, with enough detail to generate an image that matches the request. 26 | - **Flexible and varied**: Use the user's description to dynamically create prompts without following rigid templates. Ensure prompts are adapted based on the specific needs of each user, avoiding overly template-based outputs. 27 | - **Descriptions following Flux.1 style**: Prompts must follow the requirements of Flux.1, aiming to include descriptions of the art style, visual effects, and emotional atmosphere. Use keywords and description patterns that match the Flux.1 model's generation process. If a specific aspect ratio is mentioned, ensure it is included in the prompt description. 28 | 29 | 3. **Key Points Summary for Flux.1 Prompts**: 30 | - **Concise and precise subject description**: Clearly identify the subject or scene of the image. 31 | - **Specific description of style and emotional atmosphere**: Ensure the prompt includes information about the art style, lighting, color scheme, and emotional atmosphere of the image. 32 | - **Details on dynamics and action**: Prompts may include important details like actions, emotions, or lighting effects in the scene.` 33 | }; 34 | } 35 | 36 | let CONFIG; 37 | 38 | async function handleRequest(request, env, ctx) { 39 | CONFIG = initConfig(env); 40 | const url = new URL(request.url); 41 | 42 | if (request.method === "GET" && (url.pathname === "/" || url.pathname === "")) { 43 | return new Response(generateWelcomePage(), { 44 | status: 200, 45 | headers: { "Content-Type": "text/html" } 46 | }); 47 | } 48 | 49 | if (url.pathname.startsWith('/image/')) { 50 | return handleImageRequest(request, env, ctx); 51 | } 52 | 53 | if (request.method === "OPTIONS") { 54 | return handleCORS(); 55 | } 56 | 57 | if (!isAuthorized(request)) { 58 | return new Response("Unauthorized", { status: 401 }); 59 | } 60 | 61 | if (url.pathname.endsWith("/v1/models")) { 62 | return handleModelsRequest(); 63 | } 64 | 65 | if (request.method !== "POST" || !url.pathname.endsWith("/v1/chat/completions")) { 66 | return new Response("Not Found", { status: 404 }); 67 | } 68 | 69 | return handleChatCompletions(request, env, ctx); 70 | } 71 | 72 | function generateWelcomePage() { 73 | return ` 74 | 75 | 76 | 77 | 78 | 79 | Flux API - Ready for Requests 80 | 109 | 110 | 111 |
112 |

Welcome to Flux API

113 |

Your Flux API is ready and waiting for requests!

114 |

This API is designed to handle specific endpoints for chat completions and image generation.

115 |

For API documentation and usage instructions, please refer to your provided documentation.

116 |
117 | 118 | 119 | `; 120 | } 121 | 122 | function addCorsHeaders(headers) { 123 | headers.set('Access-Control-Allow-Origin', '*'); 124 | headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); 125 | headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization'); 126 | } 127 | 128 | function handleCORS() { 129 | const headers = new Headers(); 130 | addCorsHeaders(headers); 131 | return new Response(null, { status: 204, headers }); 132 | } 133 | 134 | function isAuthorized(request) { 135 | const authHeader = request.headers.get("Authorization"); 136 | return authHeader && authHeader.startsWith("Bearer ") && authHeader.split(" ")[1] === CONFIG.API_KEY; 137 | } 138 | 139 | function handleModelsRequest() { 140 | const models = [{ id: CONFIG.FLUX_MODEL, object: "model" }]; 141 | const headers = new Headers({ 'Content-Type': 'application/json' }); 142 | addCorsHeaders(headers); 143 | return new Response(JSON.stringify({ data: models, object: "list" }), { headers }); 144 | } 145 | 146 | function parseRatioAndSize(prompt) { 147 | const ratios = { 148 | "1:1": { width: 1024, height: 1024 }, 149 | "1:2": { width: 512, height: 1024 }, 150 | "3:2": { width: 768, height: 512 }, 151 | "3:4": { width: 768, height: 1024 }, 152 | "16:9": { width: 1024, height: 576 }, 153 | "9:16": { width: 576, height: 1024 } 154 | }; 155 | 156 | const ratioMatch = prompt.match(/(\d+:\d+)(?:\s|$)/); 157 | if (ratioMatch) { 158 | const ratio = ratioMatch[1]; 159 | return { 160 | size: ratios[ratio] || ratios["1:1"], 161 | cleanedPrompt: prompt.replace(/\d+:\d+/, "").trim() 162 | }; 163 | } 164 | 165 | return { 166 | size: ratios["1:1"], 167 | cleanedPrompt: prompt.trim() 168 | }; 169 | } 170 | 171 | async function handleChatCompletions(request, env, ctx) { 172 | try { 173 | const data = await request.json(); 174 | let { messages, stream } = data; 175 | 176 | const userMessage = messages.filter(msg => msg.role === "user").pop(); 177 | 178 | if (!userMessage) { 179 | return new Response(JSON.stringify({ error: "未找到用户消息" }), { status: 400, headers: { 'Content-Type': 'application/json' } }); 180 | } 181 | 182 | const originalPrompt = cleanPromptString(userMessage.content); 183 | const model = CONFIG.FLUX_MODEL; 184 | const promptModel = CONFIG.PROMPT_OPTIMIZATION ? CONFIG.EXTERNAL_MODEL : "Original Prompt"; 185 | 186 | const { size: originalSize, cleanedPrompt: cleanedOriginalPrompt } = parseRatioAndSize(originalPrompt); 187 | 188 | const translatedPrompt = await getFluxPrompt(cleanedOriginalPrompt, originalSize); 189 | 190 | const imageUrl = await generateAndStoreFluxImage(model, translatedPrompt, request.url, env, ctx, originalSize); 191 | 192 | const sizeString = `${originalSize.width}x${originalSize.height}`; 193 | 194 | return handleResponse(originalPrompt, translatedPrompt, sizeString, model, imageUrl, promptModel, stream); 195 | } catch (error) { 196 | return new Response(JSON.stringify({ error: "Internal Server Error: " + error.message }), { status: 500, headers: { 'Content-Type': 'application/json' } }); 197 | } 198 | } 199 | 200 | async function getFluxPrompt(prompt, size) { 201 | if (!CONFIG.PROMPT_OPTIMIZATION) { 202 | return prompt; 203 | } 204 | 205 | const aspectRatio = `${size.width}:${size.height}`; 206 | 207 | const requestBody = { 208 | messages: [ 209 | { 210 | role: "system", 211 | content: CONFIG.SYSTEM_MESSAGE 212 | }, 213 | { 214 | role: "user", 215 | content: `Generate a prompt for an image with the aspect ratio ${aspectRatio}. The description is: ${prompt}` 216 | } 217 | ], 218 | model: CONFIG.EXTERNAL_MODEL 219 | }; 220 | 221 | try { 222 | return await getExternalPrompt(requestBody); 223 | } catch (error) { 224 | console.error('Error in getFluxPrompt:', error); 225 | return prompt; 226 | } 227 | } 228 | 229 | async function getExternalPrompt(requestBody) { 230 | const response = await fetch(`${CONFIG.EXTERNAL_API_BASE}/v1/chat/completions`, { 231 | method: 'POST', 232 | headers: { 233 | 'Authorization': `Bearer ${CONFIG.EXTERNAL_API_KEY}`, 234 | 'Content-Type': 'application/json', 235 | 'User-Agent': 'CloudflareWorker/1.0' 236 | }, 237 | body: JSON.stringify(requestBody) 238 | }); 239 | 240 | if (!response.ok) { 241 | throw new Error(`External API request failed with status ${response.status}`); 242 | } 243 | 244 | const jsonResponse = await response.json(); 245 | if (!jsonResponse.choices || jsonResponse.choices.length === 0 || !jsonResponse.choices[0].message) { 246 | throw new Error('Invalid response format from external API'); 247 | } 248 | 249 | return jsonResponse.choices[0].message.content; 250 | } 251 | 252 | async function generateAndStoreFluxImage(model, prompt, requestUrl, env, ctx, size) { 253 | const jsonBody = { 254 | prompt, 255 | num_steps: CONFIG.FLUX_NUM_STEPS, 256 | width: size.width, 257 | height: size.height 258 | }; 259 | const response = await postRequest(model, jsonBody); 260 | const jsonResponse = await response.json(); 261 | const base64ImageData = jsonResponse.result.image; 262 | 263 | const imageBuffer = base64ToArrayBuffer(base64ImageData); 264 | 265 | const key = `image_${Date.now()}_${Math.random().toString(36).substring(7)}`; 266 | 267 | await env.FLUX_CF_KV.put(key, imageBuffer, { 268 | expirationTtl: CONFIG.IMAGE_EXPIRATION, 269 | metadata: { contentType: 'image/png' } 270 | }); 271 | 272 | return `${new URL(requestUrl).origin}/image/${key}`; 273 | } 274 | 275 | function generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel) { 276 | return `🎨 Prompt Original: ${originalPrompt}\n` + 277 | `💬 Modèle de Prompt: ${promptModel}\n` + 278 | `🌐 Prompt Traduit: ${translatedPrompt}\n` + 279 | `📐 Taille de l'Image: ${size}\n` + 280 | `🌟 Image Générée avec Succès !\n` + 281 | `Voici le résultat :\n\n` + 282 | `![Image Générée](${imageUrl})`; 283 | } 284 | 285 | function handleResponse(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel, isStream) { 286 | const content = generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel); 287 | const commonFields = { 288 | id: `chatcmpl-${Date.now()}`, 289 | created: Math.floor(Date.now() / 1000), 290 | model: model 291 | }; 292 | 293 | if (isStream) { 294 | const encoder = new TextEncoder(); 295 | const stream = new ReadableStream({ 296 | start(controller) { 297 | controller.enqueue(encoder.encode(`data: ${JSON.stringify({ 298 | ...commonFields, 299 | object: "chat.completion.chunk", 300 | choices: [{ delta: { content: content }, index: 0, finish_reason: null }] 301 | })}\n\n`)); 302 | controller.enqueue(encoder.encode('data: [DONE]\n\n')); 303 | controller.close(); 304 | } 305 | }); 306 | 307 | const headers = new Headers({ 308 | "Content-Type": "text/event-stream", 309 | "Cache-Control": "no-cache", 310 | "Connection": "keep-alive" 311 | }); 312 | addCorsHeaders(headers); 313 | return new Response(stream, { headers }); 314 | } else { 315 | const response = { 316 | ...commonFields, 317 | object: "chat.completion", 318 | choices: [{ 319 | index: 0, 320 | message: { role: "assistant", content }, 321 | finish_reason: "stop" 322 | }], 323 | usage: { 324 | prompt_tokens: translatedPrompt.length, 325 | completion_tokens: content.length, 326 | total_tokens: translatedPrompt.length + content.length 327 | } 328 | }; 329 | 330 | const headers = new Headers({ 'Content-Type': 'application/json' }); 331 | addCorsHeaders(headers); 332 | return new Response(JSON.stringify(response), { headers }); 333 | } 334 | } 335 | 336 | async function handleImageRequest(request, env, ctx) { 337 | const url = new URL(request.url); 338 | const key = url.pathname.split('/').pop(); 339 | 340 | const imageData = await env.FLUX_CF_KV.get(key, 'arrayBuffer'); 341 | if (!imageData) { 342 | return new Response('Image not found', { status: 404 }); 343 | } 344 | 345 | const headers = new Headers({ 346 | 'Content-Type': 'image/png', 347 | 'Cache-Control': 'public, max-age=604800', 348 | }); 349 | addCorsHeaders(headers); 350 | return new Response(imageData, { headers }); 351 | } 352 | 353 | function base64ToArrayBuffer(base64) { 354 | const binaryString = atob(base64); 355 | const bytes = new Uint8Array(binaryString.length); 356 | for (let i = 0; i < binaryString.length; i++) { 357 | bytes[i] = binaryString.charCodeAt(i); 358 | } 359 | return bytes.buffer; 360 | } 361 | 362 | async function postRequest(model, jsonBody) { 363 | const apiUrl = `https://api.cloudflare.com/client/v4/accounts/${CONFIG.CF_ACCOUNT_ID}/ai/run/${model}`; 364 | const response = await fetch(apiUrl, { 365 | method: 'POST', 366 | headers: { 367 | 'Authorization': `Bearer ${CONFIG.CF_API_TOKEN}`, 368 | 'Content-Type': 'application/json' 369 | }, 370 | body: JSON.stringify(jsonBody) 371 | }); 372 | 373 | if (!response.ok) { 374 | throw new Error('Cloudflare API request failed: ' + response.status); 375 | } 376 | return response; 377 | } 378 | 379 | function cleanPromptString(prompt) { 380 | return prompt.replace(/---n?tl/, "").trim(); 381 | } 382 | 383 | export default { 384 | async fetch(request, env, ctx) { 385 | CONFIG = initConfig(env); 386 | return handleRequest(request, env, ctx); 387 | } 388 | }; 389 | -------------------------------------------------------------------------------- /src/flux-api-worker_ja.js: -------------------------------------------------------------------------------- 1 | function initConfig(env) { 2 | return { 3 | API_KEY: env.API_KEY, 4 | CF_ACCOUNT_ID: env.CF_ACCOUNT_ID, 5 | CF_API_TOKEN: env.CF_API_TOKEN, 6 | PROMPT_OPTIMIZATION: env.PROMPT_OPTIMIZATION === 'true', 7 | EXTERNAL_API_BASE: env.EXTERNAL_API_BASE, 8 | EXTERNAL_MODEL: env.EXTERNAL_MODEL, 9 | EXTERNAL_API_KEY: env.EXTERNAL_API_KEY, 10 | FLUX_NUM_STEPS: parseInt(env.FLUX_NUM_STEPS, 10) || 4, 11 | FLUX_MODEL: env.FLUX_MODEL || "@cf/black-forest-labs/flux-1-schnell", 12 | IMAGE_EXPIRATION: parseInt(env.IMAGE_EXPIRATION, 10) || 60 * 30, 13 | SYSTEM_MESSAGE: env.SYSTEM_MESSAGE || `You are a prompt generation bot based on the Flux.1 model. Based on the user's requirements, automatically generate drawing prompts that adhere to the Flux.1 format. While you can refer to the provided templates to learn the structure and patterns of the prompts, you must remain flexible to meet various different needs. The final output should be limited to the prompts only, without any additional explanations or information. You must reply to me entirely in English! 14 | 15 | ### **Prompt Generation Logic**: 16 | 17 | 1. **Requirement Analysis**: Extract key information from the user's description, including: 18 | - Characters: Appearance, actions, expressions, etc. 19 | - Scene: Environment, lighting, weather, etc. 20 | - Style: Art style, emotional atmosphere, color scheme, etc. 21 | - **Aspect Ratio**: If the user provides a specific aspect ratio (e.g., "3:2", "16:9"), extract this and integrate it into the final prompt. 22 | - Other elements: Specific objects, background, or effects. 23 | 24 | 2. **Prompt Structure Guidelines**: 25 | - **Concise, precise, and detailed**: Prompts should describe the core subject simply and clearly, with enough detail to generate an image that matches the request. 26 | - **Flexible and varied**: Use the user's description to dynamically create prompts without following rigid templates. Ensure prompts are adapted based on the specific needs of each user, avoiding overly template-based outputs. 27 | - **Descriptions following Flux.1 style**: Prompts must follow the requirements of Flux.1, aiming to include descriptions of the art style, visual effects, and emotional atmosphere. Use keywords and description patterns that match the Flux.1 model's generation process. If a specific aspect ratio is mentioned, ensure it is included in the prompt description. 28 | 29 | 3. **Key Points Summary for Flux.1 Prompts**: 30 | - **Concise and precise subject description**: Clearly identify the subject or scene of the image. 31 | - **Specific description of style and emotional atmosphere**: Ensure the prompt includes information about the art style, lighting, color scheme, and emotional atmosphere of the image. 32 | - **Details on dynamics and action**: Prompts may include important details like actions, emotions, or lighting effects in the scene.` 33 | }; 34 | } 35 | 36 | let CONFIG; 37 | 38 | async function handleRequest(request, env, ctx) { 39 | CONFIG = initConfig(env); 40 | const url = new URL(request.url); 41 | 42 | if (request.method === "GET" && (url.pathname === "/" || url.pathname === "")) { 43 | return new Response(generateWelcomePage(), { 44 | status: 200, 45 | headers: { "Content-Type": "text/html" } 46 | }); 47 | } 48 | 49 | if (url.pathname.startsWith('/image/')) { 50 | return handleImageRequest(request, env, ctx); 51 | } 52 | 53 | if (request.method === "OPTIONS") { 54 | return handleCORS(); 55 | } 56 | 57 | if (!isAuthorized(request)) { 58 | return new Response("Unauthorized", { status: 401 }); 59 | } 60 | 61 | if (url.pathname.endsWith("/v1/models")) { 62 | return handleModelsRequest(); 63 | } 64 | 65 | if (request.method !== "POST" || !url.pathname.endsWith("/v1/chat/completions")) { 66 | return new Response("Not Found", { status: 404 }); 67 | } 68 | 69 | return handleChatCompletions(request, env, ctx); 70 | } 71 | 72 | function generateWelcomePage() { 73 | return ` 74 | 75 | 76 | 77 | 78 | 79 | Flux API - Ready for Requests 80 | 109 | 110 | 111 |
112 |

Welcome to Flux API

113 |

Your Flux API is ready and waiting for requests!

114 |

This API is designed to handle specific endpoints for chat completions and image generation.

115 |

For API documentation and usage instructions, please refer to your provided documentation.

116 |
117 | 118 | 119 | `; 120 | } 121 | 122 | function addCorsHeaders(headers) { 123 | headers.set('Access-Control-Allow-Origin', '*'); 124 | headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); 125 | headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization'); 126 | } 127 | 128 | function handleCORS() { 129 | const headers = new Headers(); 130 | addCorsHeaders(headers); 131 | return new Response(null, { status: 204, headers }); 132 | } 133 | 134 | function isAuthorized(request) { 135 | const authHeader = request.headers.get("Authorization"); 136 | return authHeader && authHeader.startsWith("Bearer ") && authHeader.split(" ")[1] === CONFIG.API_KEY; 137 | } 138 | 139 | function handleModelsRequest() { 140 | const models = [{ id: CONFIG.FLUX_MODEL, object: "model" }]; 141 | const headers = new Headers({ 'Content-Type': 'application/json' }); 142 | addCorsHeaders(headers); 143 | return new Response(JSON.stringify({ data: models, object: "list" }), { headers }); 144 | } 145 | 146 | function parseRatioAndSize(prompt) { 147 | const ratios = { 148 | "1:1": { width: 1024, height: 1024 }, 149 | "1:2": { width: 512, height: 1024 }, 150 | "3:2": { width: 768, height: 512 }, 151 | "3:4": { width: 768, height: 1024 }, 152 | "16:9": { width: 1024, height: 576 }, 153 | "9:16": { width: 576, height: 1024 } 154 | }; 155 | 156 | const ratioMatch = prompt.match(/(\d+:\d+)(?:\s|$)/); 157 | if (ratioMatch) { 158 | const ratio = ratioMatch[1]; 159 | return { 160 | size: ratios[ratio] || ratios["1:1"], 161 | cleanedPrompt: prompt.replace(/\d+:\d+/, "").trim() 162 | }; 163 | } 164 | 165 | return { 166 | size: ratios["1:1"], 167 | cleanedPrompt: prompt.trim() 168 | }; 169 | } 170 | 171 | async function handleChatCompletions(request, env, ctx) { 172 | try { 173 | const data = await request.json(); 174 | let { messages, stream } = data; 175 | 176 | const userMessage = messages.filter(msg => msg.role === "user").pop(); 177 | 178 | if (!userMessage) { 179 | return new Response(JSON.stringify({ error: "未找到用户消息" }), { status: 400, headers: { 'Content-Type': 'application/json' } }); 180 | } 181 | 182 | const originalPrompt = cleanPromptString(userMessage.content); 183 | const model = CONFIG.FLUX_MODEL; 184 | const promptModel = CONFIG.PROMPT_OPTIMIZATION ? CONFIG.EXTERNAL_MODEL : "Original Prompt"; 185 | 186 | const { size: originalSize, cleanedPrompt: cleanedOriginalPrompt } = parseRatioAndSize(originalPrompt); 187 | 188 | const translatedPrompt = await getFluxPrompt(cleanedOriginalPrompt, originalSize); 189 | 190 | const imageUrl = await generateAndStoreFluxImage(model, translatedPrompt, request.url, env, ctx, originalSize); 191 | 192 | const sizeString = `${originalSize.width}x${originalSize.height}`; 193 | 194 | return handleResponse(originalPrompt, translatedPrompt, sizeString, model, imageUrl, promptModel, stream); 195 | } catch (error) { 196 | return new Response(JSON.stringify({ error: "Internal Server Error: " + error.message }), { status: 500, headers: { 'Content-Type': 'application/json' } }); 197 | } 198 | } 199 | 200 | async function getFluxPrompt(prompt, size) { 201 | if (!CONFIG.PROMPT_OPTIMIZATION) { 202 | return prompt; 203 | } 204 | 205 | const aspectRatio = `${size.width}:${size.height}`; 206 | 207 | const requestBody = { 208 | messages: [ 209 | { 210 | role: "system", 211 | content: CONFIG.SYSTEM_MESSAGE 212 | }, 213 | { 214 | role: "user", 215 | content: `Generate a prompt for an image with the aspect ratio ${aspectRatio}. The description is: ${prompt}` 216 | } 217 | ], 218 | model: CONFIG.EXTERNAL_MODEL 219 | }; 220 | 221 | try { 222 | return await getExternalPrompt(requestBody); 223 | } catch (error) { 224 | console.error('Error in getFluxPrompt:', error); 225 | return prompt; 226 | } 227 | } 228 | 229 | async function getExternalPrompt(requestBody) { 230 | const response = await fetch(`${CONFIG.EXTERNAL_API_BASE}/v1/chat/completions`, { 231 | method: 'POST', 232 | headers: { 233 | 'Authorization': `Bearer ${CONFIG.EXTERNAL_API_KEY}`, 234 | 'Content-Type': 'application/json', 235 | 'User-Agent': 'CloudflareWorker/1.0' 236 | }, 237 | body: JSON.stringify(requestBody) 238 | }); 239 | 240 | if (!response.ok) { 241 | throw new Error(`External API request failed with status ${response.status}`); 242 | } 243 | 244 | const jsonResponse = await response.json(); 245 | if (!jsonResponse.choices || jsonResponse.choices.length === 0 || !jsonResponse.choices[0].message) { 246 | throw new Error('Invalid response format from external API'); 247 | } 248 | 249 | return jsonResponse.choices[0].message.content; 250 | } 251 | 252 | async function generateAndStoreFluxImage(model, prompt, requestUrl, env, ctx, size) { 253 | const jsonBody = { 254 | prompt, 255 | num_steps: CONFIG.FLUX_NUM_STEPS, 256 | width: size.width, 257 | height: size.height 258 | }; 259 | const response = await postRequest(model, jsonBody); 260 | const jsonResponse = await response.json(); 261 | const base64ImageData = jsonResponse.result.image; 262 | 263 | const imageBuffer = base64ToArrayBuffer(base64ImageData); 264 | 265 | const key = `image_${Date.now()}_${Math.random().toString(36).substring(7)}`; 266 | 267 | await env.FLUX_CF_KV.put(key, imageBuffer, { 268 | expirationTtl: CONFIG.IMAGE_EXPIRATION, 269 | metadata: { contentType: 'image/png' } 270 | }); 271 | 272 | return `${new URL(requestUrl).origin}/image/${key}`; 273 | } 274 | 275 | function generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel) { 276 | return `🎨 元のプロンプト:${originalPrompt}\n` + 277 | `💬 プロンプトモデル:${promptModel}\n` + 278 | `🌐 翻訳後のプロンプト:${translatedPrompt}\n` + 279 | `📐 画像サイズ:${size}\n` + 280 | `🌟 画像が正常に生成されました!\n` + 281 | `以下が結果です:\n\n` + 282 | `![生成された画像](${imageUrl})`; 283 | } 284 | 285 | function handleResponse(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel, isStream) { 286 | const content = generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel); 287 | const commonFields = { 288 | id: `chatcmpl-${Date.now()}`, 289 | created: Math.floor(Date.now() / 1000), 290 | model: model 291 | }; 292 | 293 | if (isStream) { 294 | const encoder = new TextEncoder(); 295 | const stream = new ReadableStream({ 296 | start(controller) { 297 | controller.enqueue(encoder.encode(`data: ${JSON.stringify({ 298 | ...commonFields, 299 | object: "chat.completion.chunk", 300 | choices: [{ delta: { content: content }, index: 0, finish_reason: null }] 301 | })}\n\n`)); 302 | controller.enqueue(encoder.encode('data: [DONE]\n\n')); 303 | controller.close(); 304 | } 305 | }); 306 | 307 | const headers = new Headers({ 308 | "Content-Type": "text/event-stream", 309 | "Cache-Control": "no-cache", 310 | "Connection": "keep-alive" 311 | }); 312 | addCorsHeaders(headers); 313 | return new Response(stream, { headers }); 314 | } else { 315 | const response = { 316 | ...commonFields, 317 | object: "chat.completion", 318 | choices: [{ 319 | index: 0, 320 | message: { role: "assistant", content }, 321 | finish_reason: "stop" 322 | }], 323 | usage: { 324 | prompt_tokens: translatedPrompt.length, 325 | completion_tokens: content.length, 326 | total_tokens: translatedPrompt.length + content.length 327 | } 328 | }; 329 | 330 | const headers = new Headers({ 'Content-Type': 'application/json' }); 331 | addCorsHeaders(headers); 332 | return new Response(JSON.stringify(response), { headers }); 333 | } 334 | } 335 | 336 | async function handleImageRequest(request, env, ctx) { 337 | const url = new URL(request.url); 338 | const key = url.pathname.split('/').pop(); 339 | 340 | const imageData = await env.FLUX_CF_KV.get(key, 'arrayBuffer'); 341 | if (!imageData) { 342 | return new Response('Image not found', { status: 404 }); 343 | } 344 | 345 | const headers = new Headers({ 346 | 'Content-Type': 'image/png', 347 | 'Cache-Control': 'public, max-age=604800', 348 | }); 349 | addCorsHeaders(headers); 350 | return new Response(imageData, { headers }); 351 | } 352 | 353 | function base64ToArrayBuffer(base64) { 354 | const binaryString = atob(base64); 355 | const bytes = new Uint8Array(binaryString.length); 356 | for (let i = 0; i < binaryString.length; i++) { 357 | bytes[i] = binaryString.charCodeAt(i); 358 | } 359 | return bytes.buffer; 360 | } 361 | 362 | async function postRequest(model, jsonBody) { 363 | const apiUrl = `https://api.cloudflare.com/client/v4/accounts/${CONFIG.CF_ACCOUNT_ID}/ai/run/${model}`; 364 | const response = await fetch(apiUrl, { 365 | method: 'POST', 366 | headers: { 367 | 'Authorization': `Bearer ${CONFIG.CF_API_TOKEN}`, 368 | 'Content-Type': 'application/json' 369 | }, 370 | body: JSON.stringify(jsonBody) 371 | }); 372 | 373 | if (!response.ok) { 374 | throw new Error('Cloudflare API request failed: ' + response.status); 375 | } 376 | return response; 377 | } 378 | 379 | function cleanPromptString(prompt) { 380 | return prompt.replace(/---n?tl/, "").trim(); 381 | } 382 | 383 | export default { 384 | async fetch(request, env, ctx) { 385 | CONFIG = initConfig(env); 386 | return handleRequest(request, env, ctx); 387 | } 388 | }; 389 | -------------------------------------------------------------------------------- /src/flux-api-worker_ru.js: -------------------------------------------------------------------------------- 1 | function initConfig(env) { 2 | return { 3 | API_KEY: env.API_KEY, 4 | CF_ACCOUNT_ID: env.CF_ACCOUNT_ID, 5 | CF_API_TOKEN: env.CF_API_TOKEN, 6 | PROMPT_OPTIMIZATION: env.PROMPT_OPTIMIZATION === 'true', 7 | EXTERNAL_API_BASE: env.EXTERNAL_API_BASE, 8 | EXTERNAL_MODEL: env.EXTERNAL_MODEL, 9 | EXTERNAL_API_KEY: env.EXTERNAL_API_KEY, 10 | FLUX_NUM_STEPS: parseInt(env.FLUX_NUM_STEPS, 10) || 4, 11 | FLUX_MODEL: env.FLUX_MODEL || "@cf/black-forest-labs/flux-1-schnell", 12 | IMAGE_EXPIRATION: parseInt(env.IMAGE_EXPIRATION, 10) || 60 * 30, 13 | SYSTEM_MESSAGE: env.SYSTEM_MESSAGE || `You are a prompt generation bot based on the Flux.1 model. Based on the user's requirements, automatically generate drawing prompts that adhere to the Flux.1 format. While you can refer to the provided templates to learn the structure and patterns of the prompts, you must remain flexible to meet various different needs. The final output should be limited to the prompts only, without any additional explanations or information. You must reply to me entirely in English! 14 | 15 | ### **Prompt Generation Logic**: 16 | 17 | 1. **Requirement Analysis**: Extract key information from the user's description, including: 18 | - Characters: Appearance, actions, expressions, etc. 19 | - Scene: Environment, lighting, weather, etc. 20 | - Style: Art style, emotional atmosphere, color scheme, etc. 21 | - **Aspect Ratio**: If the user provides a specific aspect ratio (e.g., "3:2", "16:9"), extract this and integrate it into the final prompt. 22 | - Other elements: Specific objects, background, or effects. 23 | 24 | 2. **Prompt Structure Guidelines**: 25 | - **Concise, precise, and detailed**: Prompts should describe the core subject simply and clearly, with enough detail to generate an image that matches the request. 26 | - **Flexible and varied**: Use the user's description to dynamically create prompts without following rigid templates. Ensure prompts are adapted based on the specific needs of each user, avoiding overly template-based outputs. 27 | - **Descriptions following Flux.1 style**: Prompts must follow the requirements of Flux.1, aiming to include descriptions of the art style, visual effects, and emotional atmosphere. Use keywords and description patterns that match the Flux.1 model's generation process. If a specific aspect ratio is mentioned, ensure it is included in the prompt description. 28 | 29 | 3. **Key Points Summary for Flux.1 Prompts**: 30 | - **Concise and precise subject description**: Clearly identify the subject or scene of the image. 31 | - **Specific description of style and emotional atmosphere**: Ensure the prompt includes information about the art style, lighting, color scheme, and emotional atmosphere of the image. 32 | - **Details on dynamics and action**: Prompts may include important details like actions, emotions, or lighting effects in the scene.` 33 | }; 34 | } 35 | 36 | let CONFIG; 37 | 38 | async function handleRequest(request, env, ctx) { 39 | CONFIG = initConfig(env); 40 | const url = new URL(request.url); 41 | 42 | if (request.method === "GET" && (url.pathname === "/" || url.pathname === "")) { 43 | return new Response(generateWelcomePage(), { 44 | status: 200, 45 | headers: { "Content-Type": "text/html" } 46 | }); 47 | } 48 | 49 | if (url.pathname.startsWith('/image/')) { 50 | return handleImageRequest(request, env, ctx); 51 | } 52 | 53 | if (request.method === "OPTIONS") { 54 | return handleCORS(); 55 | } 56 | 57 | if (!isAuthorized(request)) { 58 | return new Response("Unauthorized", { status: 401 }); 59 | } 60 | 61 | if (url.pathname.endsWith("/v1/models")) { 62 | return handleModelsRequest(); 63 | } 64 | 65 | if (request.method !== "POST" || !url.pathname.endsWith("/v1/chat/completions")) { 66 | return new Response("Not Found", { status: 404 }); 67 | } 68 | 69 | return handleChatCompletions(request, env, ctx); 70 | } 71 | 72 | function generateWelcomePage() { 73 | return ` 74 | 75 | 76 | 77 | 78 | 79 | Flux API - Ready for Requests 80 | 109 | 110 | 111 |
112 |

Welcome to Flux API

113 |

Your Flux API is ready and waiting for requests!

114 |

This API is designed to handle specific endpoints for chat completions and image generation.

115 |

For API documentation and usage instructions, please refer to your provided documentation.

116 |
117 | 118 | 119 | `; 120 | } 121 | 122 | function addCorsHeaders(headers) { 123 | headers.set('Access-Control-Allow-Origin', '*'); 124 | headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); 125 | headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization'); 126 | } 127 | 128 | function handleCORS() { 129 | const headers = new Headers(); 130 | addCorsHeaders(headers); 131 | return new Response(null, { status: 204, headers }); 132 | } 133 | 134 | function isAuthorized(request) { 135 | const authHeader = request.headers.get("Authorization"); 136 | return authHeader && authHeader.startsWith("Bearer ") && authHeader.split(" ")[1] === CONFIG.API_KEY; 137 | } 138 | 139 | function handleModelsRequest() { 140 | const models = [{ id: CONFIG.FLUX_MODEL, object: "model" }]; 141 | const headers = new Headers({ 'Content-Type': 'application/json' }); 142 | addCorsHeaders(headers); 143 | return new Response(JSON.stringify({ data: models, object: "list" }), { headers }); 144 | } 145 | 146 | function parseRatioAndSize(prompt) { 147 | const ratios = { 148 | "1:1": { width: 1024, height: 1024 }, 149 | "1:2": { width: 512, height: 1024 }, 150 | "3:2": { width: 768, height: 512 }, 151 | "3:4": { width: 768, height: 1024 }, 152 | "16:9": { width: 1024, height: 576 }, 153 | "9:16": { width: 576, height: 1024 } 154 | }; 155 | 156 | const ratioMatch = prompt.match(/(\d+:\d+)(?:\s|$)/); 157 | if (ratioMatch) { 158 | const ratio = ratioMatch[1]; 159 | return { 160 | size: ratios[ratio] || ratios["1:1"], 161 | cleanedPrompt: prompt.replace(/\d+:\d+/, "").trim() 162 | }; 163 | } 164 | 165 | return { 166 | size: ratios["1:1"], 167 | cleanedPrompt: prompt.trim() 168 | }; 169 | } 170 | 171 | async function handleChatCompletions(request, env, ctx) { 172 | try { 173 | const data = await request.json(); 174 | let { messages, stream } = data; 175 | 176 | const userMessage = messages.filter(msg => msg.role === "user").pop(); 177 | 178 | if (!userMessage) { 179 | return new Response(JSON.stringify({ error: "未找到用户消息" }), { status: 400, headers: { 'Content-Type': 'application/json' } }); 180 | } 181 | 182 | const originalPrompt = cleanPromptString(userMessage.content); 183 | const model = CONFIG.FLUX_MODEL; 184 | const promptModel = CONFIG.PROMPT_OPTIMIZATION ? CONFIG.EXTERNAL_MODEL : "Original Prompt"; 185 | 186 | const { size: originalSize, cleanedPrompt: cleanedOriginalPrompt } = parseRatioAndSize(originalPrompt); 187 | 188 | const translatedPrompt = await getFluxPrompt(cleanedOriginalPrompt, originalSize); 189 | 190 | const imageUrl = await generateAndStoreFluxImage(model, translatedPrompt, request.url, env, ctx, originalSize); 191 | 192 | const sizeString = `${originalSize.width}x${originalSize.height}`; 193 | 194 | return handleResponse(originalPrompt, translatedPrompt, sizeString, model, imageUrl, promptModel, stream); 195 | } catch (error) { 196 | return new Response(JSON.stringify({ error: "Internal Server Error: " + error.message }), { status: 500, headers: { 'Content-Type': 'application/json' } }); 197 | } 198 | } 199 | 200 | async function getFluxPrompt(prompt, size) { 201 | if (!CONFIG.PROMPT_OPTIMIZATION) { 202 | return prompt; 203 | } 204 | 205 | const aspectRatio = `${size.width}:${size.height}`; 206 | 207 | const requestBody = { 208 | messages: [ 209 | { 210 | role: "system", 211 | content: CONFIG.SYSTEM_MESSAGE 212 | }, 213 | { 214 | role: "user", 215 | content: `Generate a prompt for an image with the aspect ratio ${aspectRatio}. The description is: ${prompt}` 216 | } 217 | ], 218 | model: CONFIG.EXTERNAL_MODEL 219 | }; 220 | 221 | try { 222 | return await getExternalPrompt(requestBody); 223 | } catch (error) { 224 | console.error('Error in getFluxPrompt:', error); 225 | return prompt; 226 | } 227 | } 228 | 229 | async function getExternalPrompt(requestBody) { 230 | const response = await fetch(`${CONFIG.EXTERNAL_API_BASE}/v1/chat/completions`, { 231 | method: 'POST', 232 | headers: { 233 | 'Authorization': `Bearer ${CONFIG.EXTERNAL_API_KEY}`, 234 | 'Content-Type': 'application/json', 235 | 'User-Agent': 'CloudflareWorker/1.0' 236 | }, 237 | body: JSON.stringify(requestBody) 238 | }); 239 | 240 | if (!response.ok) { 241 | throw new Error(`External API request failed with status ${response.status}`); 242 | } 243 | 244 | const jsonResponse = await response.json(); 245 | if (!jsonResponse.choices || jsonResponse.choices.length === 0 || !jsonResponse.choices[0].message) { 246 | throw new Error('Invalid response format from external API'); 247 | } 248 | 249 | return jsonResponse.choices[0].message.content; 250 | } 251 | 252 | async function generateAndStoreFluxImage(model, prompt, requestUrl, env, ctx, size) { 253 | const jsonBody = { 254 | prompt, 255 | num_steps: CONFIG.FLUX_NUM_STEPS, 256 | width: size.width, 257 | height: size.height 258 | }; 259 | const response = await postRequest(model, jsonBody); 260 | const jsonResponse = await response.json(); 261 | const base64ImageData = jsonResponse.result.image; 262 | 263 | const imageBuffer = base64ToArrayBuffer(base64ImageData); 264 | 265 | const key = `image_${Date.now()}_${Math.random().toString(36).substring(7)}`; 266 | 267 | await env.FLUX_CF_KV.put(key, imageBuffer, { 268 | expirationTtl: CONFIG.IMAGE_EXPIRATION, 269 | metadata: { contentType: 'image/png' } 270 | }); 271 | 272 | return `${new URL(requestUrl).origin}/image/${key}`; 273 | } 274 | 275 | function generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel) { 276 | return `🎨 Исходный запрос: ${originalPrompt}\n` + 277 | `💬 Модель запроса: ${promptModel}\n` + 278 | `🌐 Переведённый запрос: ${translatedPrompt}\n` + 279 | `📐 Размер изображения: ${size}\n` + 280 | `🌟 Изображение успешно создано!\n` + 281 | `Вот результат:\n\n` + 282 | `![Созданное изображение](${imageUrl})`; 283 | } 284 | 285 | function handleResponse(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel, isStream) { 286 | const content = generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel); 287 | const commonFields = { 288 | id: `chatcmpl-${Date.now()}`, 289 | created: Math.floor(Date.now() / 1000), 290 | model: model 291 | }; 292 | 293 | if (isStream) { 294 | const encoder = new TextEncoder(); 295 | const stream = new ReadableStream({ 296 | start(controller) { 297 | controller.enqueue(encoder.encode(`data: ${JSON.stringify({ 298 | ...commonFields, 299 | object: "chat.completion.chunk", 300 | choices: [{ delta: { content: content }, index: 0, finish_reason: null }] 301 | })}\n\n`)); 302 | controller.enqueue(encoder.encode('data: [DONE]\n\n')); 303 | controller.close(); 304 | } 305 | }); 306 | 307 | const headers = new Headers({ 308 | "Content-Type": "text/event-stream", 309 | "Cache-Control": "no-cache", 310 | "Connection": "keep-alive" 311 | }); 312 | addCorsHeaders(headers); 313 | return new Response(stream, { headers }); 314 | } else { 315 | const response = { 316 | ...commonFields, 317 | object: "chat.completion", 318 | choices: [{ 319 | index: 0, 320 | message: { role: "assistant", content }, 321 | finish_reason: "stop" 322 | }], 323 | usage: { 324 | prompt_tokens: translatedPrompt.length, 325 | completion_tokens: content.length, 326 | total_tokens: translatedPrompt.length + content.length 327 | } 328 | }; 329 | 330 | const headers = new Headers({ 'Content-Type': 'application/json' }); 331 | addCorsHeaders(headers); 332 | return new Response(JSON.stringify(response), { headers }); 333 | } 334 | } 335 | 336 | async function handleImageRequest(request, env, ctx) { 337 | const url = new URL(request.url); 338 | const key = url.pathname.split('/').pop(); 339 | 340 | const imageData = await env.FLUX_CF_KV.get(key, 'arrayBuffer'); 341 | if (!imageData) { 342 | return new Response('Image not found', { status: 404 }); 343 | } 344 | 345 | const headers = new Headers({ 346 | 'Content-Type': 'image/png', 347 | 'Cache-Control': 'public, max-age=604800', 348 | }); 349 | addCorsHeaders(headers); 350 | return new Response(imageData, { headers }); 351 | } 352 | 353 | function base64ToArrayBuffer(base64) { 354 | const binaryString = atob(base64); 355 | const bytes = new Uint8Array(binaryString.length); 356 | for (let i = 0; i < binaryString.length; i++) { 357 | bytes[i] = binaryString.charCodeAt(i); 358 | } 359 | return bytes.buffer; 360 | } 361 | 362 | async function postRequest(model, jsonBody) { 363 | const apiUrl = `https://api.cloudflare.com/client/v4/accounts/${CONFIG.CF_ACCOUNT_ID}/ai/run/${model}`; 364 | const response = await fetch(apiUrl, { 365 | method: 'POST', 366 | headers: { 367 | 'Authorization': `Bearer ${CONFIG.CF_API_TOKEN}`, 368 | 'Content-Type': 'application/json' 369 | }, 370 | body: JSON.stringify(jsonBody) 371 | }); 372 | 373 | if (!response.ok) { 374 | throw new Error('Cloudflare API request failed: ' + response.status); 375 | } 376 | return response; 377 | } 378 | 379 | function cleanPromptString(prompt) { 380 | return prompt.replace(/---n?tl/, "").trim(); 381 | } 382 | 383 | export default { 384 | async fetch(request, env, ctx) { 385 | CONFIG = initConfig(env); 386 | return handleRequest(request, env, ctx); 387 | } 388 | }; 389 | -------------------------------------------------------------------------------- /src/flux-api-worker_zh-cn.js: -------------------------------------------------------------------------------- 1 | function initConfig(env) { 2 | return { 3 | API_KEY: env.API_KEY, 4 | CF_ACCOUNT_ID: env.CF_ACCOUNT_ID, 5 | CF_API_TOKEN: env.CF_API_TOKEN, 6 | PROMPT_OPTIMIZATION: env.PROMPT_OPTIMIZATION === 'true', 7 | EXTERNAL_API_BASE: env.EXTERNAL_API_BASE, 8 | EXTERNAL_MODEL: env.EXTERNAL_MODEL, 9 | EXTERNAL_API_KEY: env.EXTERNAL_API_KEY, 10 | FLUX_NUM_STEPS: parseInt(env.FLUX_NUM_STEPS, 10) || 4, 11 | FLUX_MODEL: env.FLUX_MODEL || "@cf/black-forest-labs/flux-1-schnell", 12 | IMAGE_EXPIRATION: parseInt(env.IMAGE_EXPIRATION, 10) || 60 * 30, 13 | SYSTEM_MESSAGE: env.SYSTEM_MESSAGE || `You are a prompt generation bot based on the Flux.1 model. Based on the user's requirements, automatically generate drawing prompts that adhere to the Flux.1 format. While you can refer to the provided templates to learn the structure and patterns of the prompts, you must remain flexible to meet various different needs. The final output should be limited to the prompts only, without any additional explanations or information. You must reply to me entirely in English! 14 | 15 | ### **Prompt Generation Logic**: 16 | 17 | 1. **Requirement Analysis**: Extract key information from the user's description, including: 18 | - Characters: Appearance, actions, expressions, etc. 19 | - Scene: Environment, lighting, weather, etc. 20 | - Style: Art style, emotional atmosphere, color scheme, etc. 21 | - **Aspect Ratio**: If the user provides a specific aspect ratio (e.g., "3:2", "16:9"), extract this and integrate it into the final prompt. 22 | - Other elements: Specific objects, background, or effects. 23 | 24 | 2. **Prompt Structure Guidelines**: 25 | - **Concise, precise, and detailed**: Prompts should describe the core subject simply and clearly, with enough detail to generate an image that matches the request. 26 | - **Flexible and varied**: Use the user's description to dynamically create prompts without following rigid templates. Ensure prompts are adapted based on the specific needs of each user, avoiding overly template-based outputs. 27 | - **Descriptions following Flux.1 style**: Prompts must follow the requirements of Flux.1, aiming to include descriptions of the art style, visual effects, and emotional atmosphere. Use keywords and description patterns that match the Flux.1 model's generation process. If a specific aspect ratio is mentioned, ensure it is included in the prompt description. 28 | 29 | 3. **Key Points Summary for Flux.1 Prompts**: 30 | - **Concise and precise subject description**: Clearly identify the subject or scene of the image. 31 | - **Specific description of style and emotional atmosphere**: Ensure the prompt includes information about the art style, lighting, color scheme, and emotional atmosphere of the image. 32 | - **Details on dynamics and action**: Prompts may include important details like actions, emotions, or lighting effects in the scene.` 33 | }; 34 | } 35 | 36 | let CONFIG; 37 | 38 | async function handleRequest(request, env, ctx) { 39 | CONFIG = initConfig(env); 40 | const url = new URL(request.url); 41 | 42 | if (request.method === "GET" && (url.pathname === "/" || url.pathname === "")) { 43 | return new Response(generateWelcomePage(), { 44 | status: 200, 45 | headers: { "Content-Type": "text/html" } 46 | }); 47 | } 48 | 49 | if (url.pathname.startsWith('/image/')) { 50 | return handleImageRequest(request, env, ctx); 51 | } 52 | 53 | if (request.method === "OPTIONS") { 54 | return handleCORS(); 55 | } 56 | 57 | if (!isAuthorized(request)) { 58 | return new Response("Unauthorized", { status: 401 }); 59 | } 60 | 61 | if (url.pathname.endsWith("/v1/models")) { 62 | return handleModelsRequest(); 63 | } 64 | 65 | if (request.method !== "POST" || !url.pathname.endsWith("/v1/chat/completions")) { 66 | return new Response("Not Found", { status: 404 }); 67 | } 68 | 69 | return handleChatCompletions(request, env, ctx); 70 | } 71 | 72 | function generateWelcomePage() { 73 | return ` 74 | 75 | 76 | 77 | 78 | 79 | Flux API - Ready for Requests 80 | 109 | 110 | 111 |
112 |

Welcome to Flux API

113 |

Your Flux API is ready and waiting for requests!

114 |

This API is designed to handle specific endpoints for chat completions and image generation.

115 |

For API documentation and usage instructions, please refer to your provided documentation.

116 |
117 | 118 | 119 | `; 120 | } 121 | 122 | function addCorsHeaders(headers) { 123 | headers.set('Access-Control-Allow-Origin', '*'); 124 | headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); 125 | headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization'); 126 | } 127 | 128 | function handleCORS() { 129 | const headers = new Headers(); 130 | addCorsHeaders(headers); 131 | return new Response(null, { status: 204, headers }); 132 | } 133 | 134 | function isAuthorized(request) { 135 | const authHeader = request.headers.get("Authorization"); 136 | return authHeader && authHeader.startsWith("Bearer ") && authHeader.split(" ")[1] === CONFIG.API_KEY; 137 | } 138 | 139 | function handleModelsRequest() { 140 | const models = [{ id: CONFIG.FLUX_MODEL, object: "model" }]; 141 | const headers = new Headers({ 'Content-Type': 'application/json' }); 142 | addCorsHeaders(headers); 143 | return new Response(JSON.stringify({ data: models, object: "list" }), { headers }); 144 | } 145 | 146 | function parseRatioAndSize(prompt) { 147 | const ratios = { 148 | "1:1": { width: 1024, height: 1024 }, 149 | "1:2": { width: 512, height: 1024 }, 150 | "3:2": { width: 768, height: 512 }, 151 | "3:4": { width: 768, height: 1024 }, 152 | "16:9": { width: 1024, height: 576 }, 153 | "9:16": { width: 576, height: 1024 } 154 | }; 155 | 156 | const ratioMatch = prompt.match(/(\d+:\d+)(?:\s|$)/); 157 | if (ratioMatch) { 158 | const ratio = ratioMatch[1]; 159 | return { 160 | size: ratios[ratio] || ratios["1:1"], 161 | cleanedPrompt: prompt.replace(/\d+:\d+/, "").trim() 162 | }; 163 | } 164 | 165 | return { 166 | size: ratios["1:1"], 167 | cleanedPrompt: prompt.trim() 168 | }; 169 | } 170 | 171 | async function handleChatCompletions(request, env, ctx) { 172 | try { 173 | const data = await request.json(); 174 | let { messages, stream } = data; 175 | 176 | const userMessage = messages.filter(msg => msg.role === "user").pop(); 177 | 178 | if (!userMessage) { 179 | return new Response(JSON.stringify({ error: "未找到用户消息" }), { status: 400, headers: { 'Content-Type': 'application/json' } }); 180 | } 181 | 182 | const originalPrompt = cleanPromptString(userMessage.content); 183 | const model = CONFIG.FLUX_MODEL; 184 | const promptModel = CONFIG.PROMPT_OPTIMIZATION ? CONFIG.EXTERNAL_MODEL : "Original Prompt"; 185 | 186 | const { size: originalSize, cleanedPrompt: cleanedOriginalPrompt } = parseRatioAndSize(originalPrompt); 187 | 188 | const translatedPrompt = await getFluxPrompt(cleanedOriginalPrompt, originalSize); 189 | 190 | const imageUrl = await generateAndStoreFluxImage(model, translatedPrompt, request.url, env, ctx, originalSize); 191 | 192 | const sizeString = `${originalSize.width}x${originalSize.height}`; 193 | 194 | return handleResponse(originalPrompt, translatedPrompt, sizeString, model, imageUrl, promptModel, stream); 195 | } catch (error) { 196 | return new Response(JSON.stringify({ error: "Internal Server Error: " + error.message }), { status: 500, headers: { 'Content-Type': 'application/json' } }); 197 | } 198 | } 199 | 200 | async function getFluxPrompt(prompt, size) { 201 | if (!CONFIG.PROMPT_OPTIMIZATION) { 202 | return prompt; 203 | } 204 | 205 | const aspectRatio = `${size.width}:${size.height}`; 206 | 207 | const requestBody = { 208 | messages: [ 209 | { 210 | role: "system", 211 | content: CONFIG.SYSTEM_MESSAGE 212 | }, 213 | { 214 | role: "user", 215 | content: `Generate a prompt for an image with the aspect ratio ${aspectRatio}. The description is: ${prompt}` 216 | } 217 | ], 218 | model: CONFIG.EXTERNAL_MODEL 219 | }; 220 | 221 | try { 222 | return await getExternalPrompt(requestBody); 223 | } catch (error) { 224 | console.error('Error in getFluxPrompt:', error); 225 | return prompt; 226 | } 227 | } 228 | 229 | async function getExternalPrompt(requestBody) { 230 | const response = await fetch(`${CONFIG.EXTERNAL_API_BASE}/v1/chat/completions`, { 231 | method: 'POST', 232 | headers: { 233 | 'Authorization': `Bearer ${CONFIG.EXTERNAL_API_KEY}`, 234 | 'Content-Type': 'application/json', 235 | 'User-Agent': 'CloudflareWorker/1.0' 236 | }, 237 | body: JSON.stringify(requestBody) 238 | }); 239 | 240 | if (!response.ok) { 241 | throw new Error(`External API request failed with status ${response.status}`); 242 | } 243 | 244 | const jsonResponse = await response.json(); 245 | if (!jsonResponse.choices || jsonResponse.choices.length === 0 || !jsonResponse.choices[0].message) { 246 | throw new Error('Invalid response format from external API'); 247 | } 248 | 249 | return jsonResponse.choices[0].message.content; 250 | } 251 | 252 | async function generateAndStoreFluxImage(model, prompt, requestUrl, env, ctx, size) { 253 | const jsonBody = { 254 | prompt, 255 | num_steps: CONFIG.FLUX_NUM_STEPS, 256 | width: size.width, 257 | height: size.height 258 | }; 259 | const response = await postRequest(model, jsonBody); 260 | const jsonResponse = await response.json(); 261 | const base64ImageData = jsonResponse.result.image; 262 | 263 | const imageBuffer = base64ToArrayBuffer(base64ImageData); 264 | 265 | const key = `image_${Date.now()}_${Math.random().toString(36).substring(7)}`; 266 | 267 | await env.FLUX_CF_KV.put(key, imageBuffer, { 268 | expirationTtl: CONFIG.IMAGE_EXPIRATION, 269 | metadata: { contentType: 'image/png' } 270 | }); 271 | 272 | return `${new URL(requestUrl).origin}/image/${key}`; 273 | } 274 | 275 | function generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel) { 276 | return `🎨 原始提示词:${originalPrompt}\n` + 277 | `💬 提示词生成模型:${promptModel}\n` + 278 | `🌐 翻译后的提示词:${translatedPrompt}\n` + 279 | `📐 图像规格:${size}\n` + 280 | `🌟 图像生成成功!\n` + 281 | `以下是结果:\n\n` + 282 | `![生成的图像](${imageUrl})`; 283 | } 284 | 285 | function handleResponse(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel, isStream) { 286 | const content = generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel); 287 | const commonFields = { 288 | id: `chatcmpl-${Date.now()}`, 289 | created: Math.floor(Date.now() / 1000), 290 | model: model 291 | }; 292 | 293 | if (isStream) { 294 | const encoder = new TextEncoder(); 295 | const stream = new ReadableStream({ 296 | start(controller) { 297 | controller.enqueue(encoder.encode(`data: ${JSON.stringify({ 298 | ...commonFields, 299 | object: "chat.completion.chunk", 300 | choices: [{ delta: { content: content }, index: 0, finish_reason: null }] 301 | })}\n\n`)); 302 | controller.enqueue(encoder.encode('data: [DONE]\n\n')); 303 | controller.close(); 304 | } 305 | }); 306 | 307 | const headers = new Headers({ 308 | "Content-Type": "text/event-stream", 309 | "Cache-Control": "no-cache", 310 | "Connection": "keep-alive" 311 | }); 312 | addCorsHeaders(headers); 313 | return new Response(stream, { headers }); 314 | } else { 315 | const response = { 316 | ...commonFields, 317 | object: "chat.completion", 318 | choices: [{ 319 | index: 0, 320 | message: { role: "assistant", content }, 321 | finish_reason: "stop" 322 | }], 323 | usage: { 324 | prompt_tokens: translatedPrompt.length, 325 | completion_tokens: content.length, 326 | total_tokens: translatedPrompt.length + content.length 327 | } 328 | }; 329 | 330 | const headers = new Headers({ 'Content-Type': 'application/json' }); 331 | addCorsHeaders(headers); 332 | return new Response(JSON.stringify(response), { headers }); 333 | } 334 | } 335 | 336 | async function handleImageRequest(request, env, ctx) { 337 | const url = new URL(request.url); 338 | const key = url.pathname.split('/').pop(); 339 | 340 | const imageData = await env.FLUX_CF_KV.get(key, 'arrayBuffer'); 341 | if (!imageData) { 342 | return new Response('Image not found', { status: 404 }); 343 | } 344 | 345 | const headers = new Headers({ 346 | 'Content-Type': 'image/png', 347 | 'Cache-Control': 'public, max-age=604800', 348 | }); 349 | addCorsHeaders(headers); 350 | return new Response(imageData, { headers }); 351 | } 352 | 353 | function base64ToArrayBuffer(base64) { 354 | const binaryString = atob(base64); 355 | const bytes = new Uint8Array(binaryString.length); 356 | for (let i = 0; i < binaryString.length; i++) { 357 | bytes[i] = binaryString.charCodeAt(i); 358 | } 359 | return bytes.buffer; 360 | } 361 | 362 | async function postRequest(model, jsonBody) { 363 | const apiUrl = `https://api.cloudflare.com/client/v4/accounts/${CONFIG.CF_ACCOUNT_ID}/ai/run/${model}`; 364 | const response = await fetch(apiUrl, { 365 | method: 'POST', 366 | headers: { 367 | 'Authorization': `Bearer ${CONFIG.CF_API_TOKEN}`, 368 | 'Content-Type': 'application/json' 369 | }, 370 | body: JSON.stringify(jsonBody) 371 | }); 372 | 373 | if (!response.ok) { 374 | throw new Error('Cloudflare API request failed: ' + response.status); 375 | } 376 | return response; 377 | } 378 | 379 | function cleanPromptString(prompt) { 380 | return prompt.replace(/---n?tl/, "").trim(); 381 | } 382 | 383 | export default { 384 | async fetch(request, env, ctx) { 385 | CONFIG = initConfig(env); 386 | return handleRequest(request, env, ctx); 387 | } 388 | }; 389 | -------------------------------------------------------------------------------- /src/flux-api-worker_zh-hant: -------------------------------------------------------------------------------- 1 | function initConfig(env) { 2 | return { 3 | API_KEY: env.API_KEY, 4 | CF_ACCOUNT_ID: env.CF_ACCOUNT_ID, 5 | CF_API_TOKEN: env.CF_API_TOKEN, 6 | PROMPT_OPTIMIZATION: env.PROMPT_OPTIMIZATION === 'true', 7 | EXTERNAL_API_BASE: env.EXTERNAL_API_BASE, 8 | EXTERNAL_MODEL: env.EXTERNAL_MODEL, 9 | EXTERNAL_API_KEY: env.EXTERNAL_API_KEY, 10 | FLUX_NUM_STEPS: parseInt(env.FLUX_NUM_STEPS, 10) || 4, 11 | FLUX_MODEL: env.FLUX_MODEL || "@cf/black-forest-labs/flux-1-schnell", 12 | IMAGE_EXPIRATION: parseInt(env.IMAGE_EXPIRATION, 10) || 60 * 30, 13 | SYSTEM_MESSAGE: env.SYSTEM_MESSAGE || `You are a prompt generation bot based on the Flux.1 model. Based on the user's requirements, automatically generate drawing prompts that adhere to the Flux.1 format. While you can refer to the provided templates to learn the structure and patterns of the prompts, you must remain flexible to meet various different needs. The final output should be limited to the prompts only, without any additional explanations or information. You must reply to me entirely in English! 14 | 15 | ### **Prompt Generation Logic**: 16 | 17 | 1. **Requirement Analysis**: Extract key information from the user's description, including: 18 | - Characters: Appearance, actions, expressions, etc. 19 | - Scene: Environment, lighting, weather, etc. 20 | - Style: Art style, emotional atmosphere, color scheme, etc. 21 | - **Aspect Ratio**: If the user provides a specific aspect ratio (e.g., "3:2", "16:9"), extract this and integrate it into the final prompt. 22 | - Other elements: Specific objects, background, or effects. 23 | 24 | 2. **Prompt Structure Guidelines**: 25 | - **Concise, precise, and detailed**: Prompts should describe the core subject simply and clearly, with enough detail to generate an image that matches the request. 26 | - **Flexible and varied**: Use the user's description to dynamically create prompts without following rigid templates. Ensure prompts are adapted based on the specific needs of each user, avoiding overly template-based outputs. 27 | - **Descriptions following Flux.1 style**: Prompts must follow the requirements of Flux.1, aiming to include descriptions of the art style, visual effects, and emotional atmosphere. Use keywords and description patterns that match the Flux.1 model's generation process. If a specific aspect ratio is mentioned, ensure it is included in the prompt description. 28 | 29 | 3. **Key Points Summary for Flux.1 Prompts**: 30 | - **Concise and precise subject description**: Clearly identify the subject or scene of the image. 31 | - **Specific description of style and emotional atmosphere**: Ensure the prompt includes information about the art style, lighting, color scheme, and emotional atmosphere of the image. 32 | - **Details on dynamics and action**: Prompts may include important details like actions, emotions, or lighting effects in the scene.` 33 | }; 34 | } 35 | 36 | let CONFIG; 37 | 38 | async function handleRequest(request, env, ctx) { 39 | CONFIG = initConfig(env); 40 | const url = new URL(request.url); 41 | 42 | if (request.method === "GET" && (url.pathname === "/" || url.pathname === "")) { 43 | return new Response(generateWelcomePage(), { 44 | status: 200, 45 | headers: { "Content-Type": "text/html" } 46 | }); 47 | } 48 | 49 | if (url.pathname.startsWith('/image/')) { 50 | return handleImageRequest(request, env, ctx); 51 | } 52 | 53 | if (request.method === "OPTIONS") { 54 | return handleCORS(); 55 | } 56 | 57 | if (!isAuthorized(request)) { 58 | return new Response("Unauthorized", { status: 401 }); 59 | } 60 | 61 | if (url.pathname.endsWith("/v1/models")) { 62 | return handleModelsRequest(); 63 | } 64 | 65 | if (request.method !== "POST" || !url.pathname.endsWith("/v1/chat/completions")) { 66 | return new Response("Not Found", { status: 404 }); 67 | } 68 | 69 | return handleChatCompletions(request, env, ctx); 70 | } 71 | 72 | function generateWelcomePage() { 73 | return ` 74 | 75 | 76 | 77 | 78 | 79 | Flux API - Ready for Requests 80 | 109 | 110 | 111 |
112 |

Welcome to Flux API

113 |

Your Flux API is ready and waiting for requests!

114 |

This API is designed to handle specific endpoints for chat completions and image generation.

115 |

For API documentation and usage instructions, please refer to your provided documentation.

116 |
117 | 118 | 119 | `; 120 | } 121 | 122 | function addCorsHeaders(headers) { 123 | headers.set('Access-Control-Allow-Origin', '*'); 124 | headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); 125 | headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization'); 126 | } 127 | 128 | function handleCORS() { 129 | const headers = new Headers(); 130 | addCorsHeaders(headers); 131 | return new Response(null, { status: 204, headers }); 132 | } 133 | 134 | function isAuthorized(request) { 135 | const authHeader = request.headers.get("Authorization"); 136 | return authHeader && authHeader.startsWith("Bearer ") && authHeader.split(" ")[1] === CONFIG.API_KEY; 137 | } 138 | 139 | function handleModelsRequest() { 140 | const models = [{ id: CONFIG.FLUX_MODEL, object: "model" }]; 141 | const headers = new Headers({ 'Content-Type': 'application/json' }); 142 | addCorsHeaders(headers); 143 | return new Response(JSON.stringify({ data: models, object: "list" }), { headers }); 144 | } 145 | 146 | function parseRatioAndSize(prompt) { 147 | const ratios = { 148 | "1:1": { width: 1024, height: 1024 }, 149 | "1:2": { width: 512, height: 1024 }, 150 | "3:2": { width: 768, height: 512 }, 151 | "3:4": { width: 768, height: 1024 }, 152 | "16:9": { width: 1024, height: 576 }, 153 | "9:16": { width: 576, height: 1024 } 154 | }; 155 | 156 | const ratioMatch = prompt.match(/(\d+:\d+)(?:\s|$)/); 157 | if (ratioMatch) { 158 | const ratio = ratioMatch[1]; 159 | return { 160 | size: ratios[ratio] || ratios["1:1"], 161 | cleanedPrompt: prompt.replace(/\d+:\d+/, "").trim() 162 | }; 163 | } 164 | 165 | return { 166 | size: ratios["1:1"], 167 | cleanedPrompt: prompt.trim() 168 | }; 169 | } 170 | 171 | async function handleChatCompletions(request, env, ctx) { 172 | try { 173 | const data = await request.json(); 174 | let { messages, stream } = data; 175 | 176 | const userMessage = messages.filter(msg => msg.role === "user").pop(); 177 | 178 | if (!userMessage) { 179 | return new Response(JSON.stringify({ error: "未找到用户消息" }), { status: 400, headers: { 'Content-Type': 'application/json' } }); 180 | } 181 | 182 | const originalPrompt = cleanPromptString(userMessage.content); 183 | const model = CONFIG.FLUX_MODEL; 184 | const promptModel = CONFIG.PROMPT_OPTIMIZATION ? CONFIG.EXTERNAL_MODEL : "Original Prompt"; 185 | 186 | const { size: originalSize, cleanedPrompt: cleanedOriginalPrompt } = parseRatioAndSize(originalPrompt); 187 | 188 | const translatedPrompt = await getFluxPrompt(cleanedOriginalPrompt, originalSize); 189 | 190 | const imageUrl = await generateAndStoreFluxImage(model, translatedPrompt, request.url, env, ctx, originalSize); 191 | 192 | const sizeString = `${originalSize.width}x${originalSize.height}`; 193 | 194 | return handleResponse(originalPrompt, translatedPrompt, sizeString, model, imageUrl, promptModel, stream); 195 | } catch (error) { 196 | return new Response(JSON.stringify({ error: "Internal Server Error: " + error.message }), { status: 500, headers: { 'Content-Type': 'application/json' } }); 197 | } 198 | } 199 | 200 | async function getFluxPrompt(prompt, size) { 201 | if (!CONFIG.PROMPT_OPTIMIZATION) { 202 | return prompt; 203 | } 204 | 205 | const aspectRatio = `${size.width}:${size.height}`; 206 | 207 | const requestBody = { 208 | messages: [ 209 | { 210 | role: "system", 211 | content: CONFIG.SYSTEM_MESSAGE 212 | }, 213 | { 214 | role: "user", 215 | content: `Generate a prompt for an image with the aspect ratio ${aspectRatio}. The description is: ${prompt}` 216 | } 217 | ], 218 | model: CONFIG.EXTERNAL_MODEL 219 | }; 220 | 221 | try { 222 | return await getExternalPrompt(requestBody); 223 | } catch (error) { 224 | console.error('Error in getFluxPrompt:', error); 225 | return prompt; 226 | } 227 | } 228 | 229 | async function getExternalPrompt(requestBody) { 230 | const response = await fetch(`${CONFIG.EXTERNAL_API_BASE}/v1/chat/completions`, { 231 | method: 'POST', 232 | headers: { 233 | 'Authorization': `Bearer ${CONFIG.EXTERNAL_API_KEY}`, 234 | 'Content-Type': 'application/json', 235 | 'User-Agent': 'CloudflareWorker/1.0' 236 | }, 237 | body: JSON.stringify(requestBody) 238 | }); 239 | 240 | if (!response.ok) { 241 | throw new Error(`External API request failed with status ${response.status}`); 242 | } 243 | 244 | const jsonResponse = await response.json(); 245 | if (!jsonResponse.choices || jsonResponse.choices.length === 0 || !jsonResponse.choices[0].message) { 246 | throw new Error('Invalid response format from external API'); 247 | } 248 | 249 | return jsonResponse.choices[0].message.content; 250 | } 251 | 252 | async function generateAndStoreFluxImage(model, prompt, requestUrl, env, ctx, size) { 253 | const jsonBody = { 254 | prompt, 255 | num_steps: CONFIG.FLUX_NUM_STEPS, 256 | width: size.width, 257 | height: size.height 258 | }; 259 | const response = await postRequest(model, jsonBody); 260 | const jsonResponse = await response.json(); 261 | const base64ImageData = jsonResponse.result.image; 262 | 263 | const imageBuffer = base64ToArrayBuffer(base64ImageData); 264 | 265 | const key = `image_${Date.now()}_${Math.random().toString(36).substring(7)}`; 266 | 267 | await env.FLUX_CF_KV.put(key, imageBuffer, { 268 | expirationTtl: CONFIG.IMAGE_EXPIRATION, 269 | metadata: { contentType: 'image/png' } 270 | }); 271 | 272 | return `${new URL(requestUrl).origin}/image/${key}`; 273 | } 274 | 275 | function generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel) { 276 | return `🎨 原始提示詞:${originalPrompt}\n` + 277 | `💬 提示詞生成模型:${promptModel}\n` + 278 | `🌐 轉換後的提示詞:${translatedPrompt}\n` + 279 | `📐 圖像規格:${size}\n` + 280 | `🌟 圖像生成成功!\n` + 281 | `以下是結果:\n\n` + 282 | `![生成的圖像](${imageUrl})`; 283 | } 284 | 285 | function handleResponse(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel, isStream) { 286 | const content = generateResponseContent(originalPrompt, translatedPrompt, size, model, imageUrl, promptModel); 287 | const commonFields = { 288 | id: `chatcmpl-${Date.now()}`, 289 | created: Math.floor(Date.now() / 1000), 290 | model: model 291 | }; 292 | 293 | if (isStream) { 294 | const encoder = new TextEncoder(); 295 | const stream = new ReadableStream({ 296 | start(controller) { 297 | controller.enqueue(encoder.encode(`data: ${JSON.stringify({ 298 | ...commonFields, 299 | object: "chat.completion.chunk", 300 | choices: [{ delta: { content: content }, index: 0, finish_reason: null }] 301 | })}\n\n`)); 302 | controller.enqueue(encoder.encode('data: [DONE]\n\n')); 303 | controller.close(); 304 | } 305 | }); 306 | 307 | const headers = new Headers({ 308 | "Content-Type": "text/event-stream", 309 | "Cache-Control": "no-cache", 310 | "Connection": "keep-alive" 311 | }); 312 | addCorsHeaders(headers); 313 | return new Response(stream, { headers }); 314 | } else { 315 | const response = { 316 | ...commonFields, 317 | object: "chat.completion", 318 | choices: [{ 319 | index: 0, 320 | message: { role: "assistant", content }, 321 | finish_reason: "stop" 322 | }], 323 | usage: { 324 | prompt_tokens: translatedPrompt.length, 325 | completion_tokens: content.length, 326 | total_tokens: translatedPrompt.length + content.length 327 | } 328 | }; 329 | 330 | const headers = new Headers({ 'Content-Type': 'application/json' }); 331 | addCorsHeaders(headers); 332 | return new Response(JSON.stringify(response), { headers }); 333 | } 334 | } 335 | 336 | async function handleImageRequest(request, env, ctx) { 337 | const url = new URL(request.url); 338 | const key = url.pathname.split('/').pop(); 339 | 340 | const imageData = await env.FLUX_CF_KV.get(key, 'arrayBuffer'); 341 | if (!imageData) { 342 | return new Response('Image not found', { status: 404 }); 343 | } 344 | 345 | const headers = new Headers({ 346 | 'Content-Type': 'image/png', 347 | 'Cache-Control': 'public, max-age=604800', 348 | }); 349 | addCorsHeaders(headers); 350 | return new Response(imageData, { headers }); 351 | } 352 | 353 | function base64ToArrayBuffer(base64) { 354 | const binaryString = atob(base64); 355 | const bytes = new Uint8Array(binaryString.length); 356 | for (let i = 0; i < binaryString.length; i++) { 357 | bytes[i] = binaryString.charCodeAt(i); 358 | } 359 | return bytes.buffer; 360 | } 361 | 362 | async function postRequest(model, jsonBody) { 363 | const apiUrl = `https://api.cloudflare.com/client/v4/accounts/${CONFIG.CF_ACCOUNT_ID}/ai/run/${model}`; 364 | const response = await fetch(apiUrl, { 365 | method: 'POST', 366 | headers: { 367 | 'Authorization': `Bearer ${CONFIG.CF_API_TOKEN}`, 368 | 'Content-Type': 'application/json' 369 | }, 370 | body: JSON.stringify(jsonBody) 371 | }); 372 | 373 | if (!response.ok) { 374 | throw new Error('Cloudflare API request failed: ' + response.status); 375 | } 376 | return response; 377 | } 378 | 379 | function cleanPromptString(prompt) { 380 | return prompt.replace(/---n?tl/, "").trim(); 381 | } 382 | 383 | export default { 384 | async fetch(request, env, ctx) { 385 | CONFIG = initConfig(env); 386 | return handleRequest(request, env, ctx); 387 | } 388 | }; 389 | --------------------------------------------------------------------------------