type
status
date
slug
summary
category
tags
icon
password
AI 摘要
1.在 Prodiap 注册一个账户,并获取 PRODIA_API_KEY:


2.在Cloud Flare中的Workers 和 Pages中创建一个Worker项目并点击编辑代码:

3.复制下方的js项目粘贴至worker中修改相应参数后部署:
(如何获取cf_accountID以及cf_token请自行查阅学习)
API_KEY可自定义比如:sk-123456789
//prodia.com API_KEY
const PRODIA_API_KEY = "XXX";
//本项目授权api_key,防止被恶意调用
const API_KEY = "sk-XXX";
//cloudflare账号列表,每次请求都会随机从列表里取一个账号
const CF_ACCOUNT_LIST = [
{ account_id: "cf_accountID", token: "cf_token" }
];
//在你输入的prompt中添加 ---ntl可强制禁止提示词翻译、优化功能
//在你输入的prompt中添加 ---tl可强制开启提示词翻译、优化功能
//是否开启提示词翻译、优化功能
const CF_IS_TRANSLATE = true;
//示词翻译、优化模型
const CF_TRANSLATE_MODEL = "@cf/qwen/qwen1.5-14b-chat-awq";
//模型映射,设置客户端可用的模型。one-api,new-api在添加渠道时可使用"获取模型列表"功能,一键添加模型
const CUSTOMER_MODEL_MAP = {
"animagineXLV3_v30.safetensors": "animagineXLV3_v30.safetensors [75f2f05b]",
"devlishphotorealism_sdxl15.safetensors": "devlishphotorealism_sdxl15.safetensors [77cba69f]",
"dreamshaperXL10_alpha2.safetensors": "dreamshaperXL10_alpha2.safetensors [c8afe2ef]",
"dynavisionXL_0411.safetensors": "dynavisionXL_0411.safetensors [c39cc051]",
"juggernautXL_v45.safetensors": "juggernautXL_v45.safetensors [e75f5471]",
"realismEngineSDXL_v10.safetensors": "realismEngineSDXL_v10.safetensors [af771c3f]",
"realvisxlV40.safetensors": "realvisxlV40.safetensors [f7fdcb51]",
"sd_xl_base_1.0.safetensors": "sd_xl_base_1.0.safetensors [be9edd61]",
"sd_xl_base_1.0_inpainting_0.1.safetensors": "sd_xl_base_1.0_inpainting_0.1.safetensors [5679a81a]",
"turbovisionXL_v431.safetensors": "turbovisionXL_v431.safetensors [78890989]"
};
/**
- Handles incoming requests to the Cloudflare Worker.
- @param {Request} request - The incoming request object.
- @returns {Response} - The response object.
- @throws {Error} - If the request is invalid or the response fails. / async function handleRequest(request) { try { if (request.method === "OPTIONS") { return new Response("", { status: 204, headers: { 'Access-Control-Allow-Origin': '', "Access-Control-Allow-Headers": '*' } }); }
const authHeader = request.headers.get("Authorization");
if (!authHeader || !authHeader.startsWith("Bearer ") || authHeader.split(" ")[1] !== API_KEY) {
return new Response("Unauthorized", { status: 401 });
}
if (request.url.endsWith("/v1/models")) {
const arrs = [];
Object.keys(CUSTOMER_MODEL_MAP).map(element => arrs.push({ id: element, object: "model" }))
const response = {
data: arrs,
success: true
};
return new Response(JSON.stringify(response), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '',
'Access-Control-Allow-Headers': ''
}
});
}
if (request.method !== "POST") {
return new Response("Only POST requests are allowed", {
status: 405,
headers: {
'Access-Control-Allow-Origin': '',
"Access-Control-Allow-Headers": ''
}
});
}
if (!request.url.endsWith("/v1/chat/completions")) {
return new Response("Not Found", {
status: 404,
headers: {
'Access-Control-Allow-Origin': '',
"Access-Control-Allow-Headers": ''
}
});
}
const data = await request.json();
const messages = data.messages || [];
const model = CUSTOMER_MODEL_MAP[data.model] || CUSTOMER_MODEL_MAP["v1-5-inpainting.safetensors"];
const stream = data.stream || false;
const userMessage = messages.reverse().find((msg) => msg.role === "user")?.content;
if (!userMessage) {
return new Response(JSON.stringify({ error: "未找到用户消息" }), {
status: 400,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '',
'Access-Control-Allow-Headers': ''
}
});
}
const is_translate = extractTranslate(userMessage);
const originalPrompt = cleanPromptString(userMessage);
const translatedPrompt = is_translate ? await getPrompt(originalPrompt) : originalPrompt;
const imageUrl = await generateImageByText(model, translatedPrompt);
if (stream) {
return handleStreamResponse(originalPrompt, translatedPrompt,"1024x1024", model, imageUrl);
} else {
return handleNonStreamResponse(originalPrompt, translatedPrompt, "1024x1024", model, imageUrl);
}
} catch (error) {
return new Response("Internal Server Error: " + error.message, {
status: 500,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '',
'Access-Control-Allow-Headers': ''
}
});
}
}
/**
- @description
- Translate a prompt into a stable diffusion prompt style.
- @param {string} prompt - The prompt to translate.
- @returns {Promise<string>} The translated prompt.
- @throws {Error} If the translation fails. */ async function getPrompt(prompt) { const requestBodyJson = { messages: [ { role: "system", content: `作为 Stable Diffusion Prompt 提示词专家,您将从关键词中创建提示,通常来自 Danbooru 等数据库。
},
{
role: "user",
content: prompt
}
]
};
const response = await postRequest(CF_TRANSLATE_MODEL, requestBodyJson);
if (!response.ok) {
return prompt;
}
const jsonResponse = await response.json();
const res = jsonResponse.result.response;
return res;
}
/**
- Generate an image from a given text prompt using the Prodia AI API
- @param {string} model - The name of the AI model to use for image generation
- @param {string} prompt - The text prompt to generate an image from
- @returns {string} - The URL of the generated image
- @throws {Error} - If the image generation fails
- @see https://docs.prodia.ai/docs/api-reference */ async function generateImageByText(model, prompt) { // First request to generate the image const generateOptions = { method: 'POST', headers: { accept: 'application/json', 'content-type': 'application/json', 'X-Prodia-Key': PRODIA_API_KEY }, body: JSON.stringify({ model: model, prompt: prompt, negative_prompt: 'low resolution, blurry, distorted features, wrong fingers, extra numbers, watermarks, ugly, distorted, deformed, deformed, repetitive, missing arms and legs, multiple hands and legs, incomplete limbs, long neck, cross-eyed, glazed eyes, lax eyes, squinting, deformed eyes', steps: 20, cfg_scale: 7, seed: -1, sampler: 'DPM++ 2M Karras', width: 1024, height: 1024 }) };
try {
const generateResponse = await fetch('https://api.prodia.com/v1/sdxl/generate', generateOptions);
const generateData = await generateResponse.json();
} catch (error) {
return "图像生成或转换失败,请检查!" + error.message;
}
}
/**
- Return a streaming response with the generated image.
- The response will contain the generated image as a base64 encoded string
- and the original and translated prompts as text. The response will be sent
- as a Server-Sent Event (SSE) stream, with the
data
event containing the
- response payload.
- @param {string} originalPrompt - The original prompt given to the model.
- @param {string} translatedPrompt - The translated prompt given to the model.
- @param {string} size - The size of the generated image.
- @param {string} model - The model used to generate the image.
- @param {string} imageUrl - The URL of the generated image.
- @returns {Response} - The response object.
*/
function handleStreamResponse(originalPrompt, translatedPrompt, size, model, imageUrl) {
const uniqueId =
chatcmpl-${Date.now()}
; const createdTimestamp = Math.floor(Date.now() / 1000); const systemFingerprint = "fp_" + Math.random().toString(36).substr(2, 9); const content =🎨 原始提示词:${originalPrompt}\\n
+🌐 翻译后的提示词:${translatedPrompt}\\n
+📐 图像规格:${size}\\n
+🌟 图像生成成功!\\n
+以下是结果:\\n\\n
+
;
const responsePayload = {
id: uniqueId,
object: "chat.completion.chunk",
created: createdTimestamp,
model: model,
system_fingerprint: systemFingerprint,
choices: [
{
index: 0,
delta: {
content: content,
},
finish_reason: "stop",
},
],
};
const dataString = JSON.stringify(responsePayload);
return new Response(
data: ${dataString}\\n\\n
, {
status: 200,
headers: {
"Content-Type": "text/event-stream",
'Access-Control-Allow-Origin': '',
"Access-Control-Allow-Headers": '',
},
});
}/**
- Return a non-streaming response with the generated image.
- The response will contain the generated image as a base64 encoded string
- and the original and translated prompts as text.
- @param {string} originalPrompt - The original prompt given to the model.
- @param {string} translatedPrompt - The translated prompt given to the model.
- @param {string} size - The size of the generated image (e.g. 1024x1024).
- @param {string} model - The model used to generate the image (e.g. @cf/stabilityai/stable-diffusion-xl-base-1.0).
- @param {string} imageUrl - The URL of the generated image.
- @return {Response} - The response object with the generated image and prompts.
*/
function handleNonStreamResponse(originalPrompt, translatedPrompt, size, model, imageUrl) {
const uniqueId =
chatcmpl-${Date.now()}
; const createdTimestamp = Math.floor(Date.now() / 1000); const systemFingerprint = "fp_" + Math.random().toString(36).substr(2, 9); const content =🎨 原始提示词:${originalPrompt}\\n
+🌐 翻译后的提示词:${translatedPrompt}\\n
+📐 图像规格:${size}\\n
+🌟 图像生成成功!\\n
+以下是结果:\\n\\n
+
;
const response = {
id: uniqueId,
object: "chat.completion",
created: createdTimestamp,
model: model,
system_fingerprint: systemFingerprint,
choices: [{
index: 0,
message: {
role: "assistant",
content: content
},
finish_reason: "stop"
}],
usage: {
prompt_tokens: translatedPrompt.length,
completion_tokens: content.length,
total_tokens: translatedPrompt.length + content.length
}
};
const dataString = JSON.stringify(response);
return new Response(dataString, {
status: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '',
'Access-Control-Allow-Headers': ''
}
});
}
/**
- @description
- POST request to Cloudflare AI API
- @param {string} model - AI model name
- @param {object} jsonBody - JSON object to be sent in the body of the request
- @returns {Promise<Response>} - Response object
- @throws {Error} - If response status is not OK
*/
async function postRequest(model, jsonBody) {
const cf_account = CF_ACCOUNT_LIST[Math.floor(Math.random() * CF_ACCOUNT_LIST.length)];
const apiUrl =
https://api.cloudflare.com/client/v4/accounts/${cf_account.account_id}/ai/run/${model}
; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Authorization':Bearer ${cf_account.token}
, 'Content-Type': 'application/json' }, body: JSON.stringify(jsonBody) });
if (!response.ok) {
throw new Error('Unexpected response ' + response.status);
}
return response;
}
/**
- Extract translate flag from prompt string.
- This function will parse the flag from the given prompt string and return the
- translate flag. If the flag is not found, it will return the default translate
- flag set in CF_IS_TRANSLATE.
- @param {string} prompt The prompt string to parse the flag from.
- @return {boolean} The translate flag parsed from the prompt string. */ function extractTranslate(prompt) { const match = prompt.match(/---n?tl/); if (match && match[0]) { if (match[0] == "---ntl") { return false; } else if (match[0] == "---tl") { return true; } } return CF_IS_TRANSLATE; }
/**
- Remove translate flag from prompt string.
- This function will remove the translate flag ("---ntl" or "---tl") from the
- given prompt string and return the cleaned prompt string.
- @param {string} prompt The prompt string to clean.
- @return {string} The cleaned prompt string. */ function cleanPromptString(prompt) { return prompt.replace(/---n?tl/, "").trim(); }
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
4.在刚刚创建的worker中点击设置,在域和路由中添加自定义域,并填写自己的子域名,如image.xxxx.com:
(此处需自行查阅学习如何给cf添加自己的域名)

5.复制自己的域名在任意对话平台或API中转站中添加自定义接口以及自定义模型:
(此处以ChatGPT-Next-Web为例)

以下是所有模型的介绍,可以根据自己的需求来选择模型:
- animagineXLV3_v30 :适合想要动画风格的创作者,可能在动画效果上表现优越。
- devlishphotorealism_sdxl15 :如果你需要超真实的图像效果,这个模型可能是个好选择,特别适合用于摄影风格的创作。
- dreamshaperXL10_alpha2 :注重创意和想象力,适合艺术风格较强的作品。
- juggernautXL_v45 :可能在生成大型复杂场景方面表现出色,可以考虑。
- realismEngineSDXL_v10 :专注于真实效果的生成,适合需要高度逼真图像的项目。
- sd_xl_base_1.0 和 sd_xl_base_1.0_inpainting_0.1 :这些是基础模型,适合多种用途,也可以在需要时进行细微调整。
- turbovisionXL_v431 :可能在速度和效率上有优势,适合需要快速生成的场景。
- 作者:Yong Liu
- 链接:https://ikiss.eu.org/article/abc/14
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章