Aigen / index.html
Bfjandak's picture
Create index.html
5f7941c verified
Raw
History Blame Contribute Delete
16.8 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Infinite-Scale Zero-Cost AI Playground</title>
<script src="https://tailwindcss.com"></script>
<style>
.custom-scrollbar::-webkit-scrollbar { width: 6px; }
.custom-scrollbar::-webkit-scrollbar-track { background: #1f2937; }
.custom-scrollbar::-webkit-scrollbar-thumb { background: #4b5563; border-radius: 3px; }
</style>
</head>
<body class="bg-gray-950 text-gray-100 min-h-screen flex flex-col font-sans">
<header class="border-b border-gray-800 bg-gray-900/50 backdrop-blur px-6 py-4 flex flex-wrap items-center justify-between gap-4">
<div class="flex items-center space-x-3">
<span class="text-2xl"></span>
<div>
<h1 class="text-xl font-bold tracking-tight bg-gradient-to-r from-yellow-400 to-orange-500 bg-clip-text text-transparent">Zero-Cost AI Engine</h1>
<p class="text-xs text-gray-400">100% Client-Side Decentralized Architecture</p>
</div>
</div>
<div class="flex items-center space-x-3">
<div id="auth-status" class="text-xs font-mono bg-gray-800 px-3 py-1.5 rounded border border-gray-700 text-gray-400">
Using Shared Anonymous IP Limits
</div>
<button id="login-btn" class="bg-yellow-500 hover:bg-yellow-600 text-gray-950 px-4 py-1.5 rounded font-medium text-sm transition shadow-lg shadow-yellow-500/10">
Link HF Account
</button>
</div>
</header>
<main class="flex-1 grid grid-cols-1 lg:grid-cols-12 gap-6 p-6 overflow-hidden">
<section class="lg:col-span-4 bg-gray-900 border border-gray-800 rounded-xl p-5 flex flex-col space-y-4 shadow-xl">
<div>
<label class="block text-xs font-semibold uppercase tracking-wider text-gray-400 mb-2">1. Choose Target Model</label>
<select id="model-selector" class="w-full bg-gray-950 border border-gray-700 rounded-lg px-3 py-2.5 text-sm focus:outline-none focus:border-yellow-500 text-gray-200">
<optgroup label="📝 Text Generation (Real-Time SSE Streaming)">
<option value="meta-llama/Llama-3.3-70B-Instruct" selected>meta-llama/Llama-3.3-70B-Instruct</option>
<option value="deepseek-ai/DeepSeek-R1-Distill-Qwen-32B">deepseek-ai/DeepSeek-R1-Distill-Qwen-32B (CoT)</option>
</optgroup>
<optgroup label="🎨 Image Generation (Binary Blob Manipulation)">
<option value="black-forest-labs/FLUX.1-schnell">black-forest-labs/FLUX.1-schnell (Ultra-Fast)</option>
<option value="stabilityai/stable-diffusion-3.5-large">stabilityai/stable-diffusion-3.5-large</option>
</optgroup>
<optgroup label="🎬 Video Generation (ZeroGPU Space Fallback Pools)">
<option value="VIDEO_POOL">Public ZeroGPU Video Space Cluster</option>
</optgroup>
</select>
</div>
<div class="flex-1 flex flex-col">
<label class="block text-xs font-semibold uppercase tracking-wider text-gray-400 mb-2">2. Enter Generation Prompt</label>
<textarea id="prompt-input" placeholder="Type instructions here..." class="w-full flex-1 min-h-[150px] bg-gray-950 border border-gray-700 rounded-lg p-3 text-sm focus:outline-none focus:border-yellow-500 resize-none custom-scrollbar text-gray-100"></textarea>
</div>
<button id="generate-btn" class="w-full bg-gradient-to-r from-yellow-500 to-orange-500 hover:from-yellow-600 hover:to-orange-600 text-gray-950 font-bold py-3 rounded-lg transition transform active:scale-[0.98] shadow-lg shadow-orange-500/20">
Execute Request
</button>
</section>
<section class="lg:col-span-8 bg-gray-900 border border-gray-800 rounded-xl p-5 flex flex-col shadow-xl min-h-[400px]">
<div class="flex items-center justify-between border-b border-gray-800 pb-3 mb-4">
<span class="text-xs font-semibold uppercase tracking-wider text-gray-400">Live Execution Output Canvas</span>
<span id="status-indicator" class="text-xs text-gray-500 font-mono">Idle</span>
</div>
<div class="flex-1 flex flex-col justify-center items-center relative overflow-hidden bg-gray-950 border border-gray-800 rounded-lg p-4">
<div id="text-output" class="w-full h-full text-sm font-mono whitespace-pre-wrap overflow-y-auto custom-scrollbar text-gray-300 hidden select-text"></div>
<img id="image-output" alt="AI Generation Output" class="max-w-full max-h-full object-contain rounded shadow-2xl hidden" />
<video id="video-output" controls class="max-w-full max-h-full rounded shadow-2xl hidden"></video>
<div id="placeholder-view" class="text-center space-y-2 pointer-events-none">
<span class="text-4xl block opacity-40">🤖</span>
<p class="text-xs text-gray-500">Output will render in real time within this client container.</p>
</div>
</div>
</section>
</main>
<script type="module">
const CONFIG = {
imageModels: [
"black-forest-labs/FLUX.1-schnell",
"stabilityai/stable-diffusion-3.5-large"
],
videoSpaces: [
"zhipuai/CogVideoX-5b-Space",
"ali-vilab/InVideo-AnimateDiff"
]
};
class ProductionHFEngine {
constructor() {
this.token = window.localStorage.getItem('hf_playground_token') || "";
}
setToken(newToken) {
this.token = newToken;
window.localStorage.setItem('hf_playground_token', newToken);
}
getHeaders() {
const headers = { "Content-Type": "application/json" };
if (this.token) headers["Authorization"] = `Bearer ${this.token}`;
return headers;
}
/**
* Handles Serverless Fallback Streams & Image Blobs
*/
async runInference(modelId, prompt, UI) {
const isImage = CONFIG.imageModels.includes(modelId);
UI.onStatus("Connecting to Serverless Pipeline...");
try {
const payload = isImage
? { inputs: prompt }
: { inputs: prompt, parameters: { max_new_tokens: 1024 }, stream: true };
const response = await fetch(`https://api-inference.huggingface.co/models/${modelId}`, {
method: "POST",
headers: this.getHeaders(),
body: JSON.stringify(payload)
});
if (response.status === 429) {
throw new Error("429 Rate Limit Hit. Provide your own free HF token or try again in 5 minutes.");
}
if (!response.ok) throw new Error(`Hugging Face Error: ${response.statusText}`);
if (isImage) {
const blob = await response.blob();
UI.onImage(URL.createObjectURL(blob));
return;
}
// Standardised Streaming Line Parser Engine
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8");
let buffer = "";
UI.onTextStart();
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop();
for (const line of lines) {
const cleanLine = line.trim();
if (!cleanLine || cleanLine.startsWith(":")) continue; // Bypasses streaming keep-alive lines
if (cleanLine.startsWith("data:")) {
const dataStr = cleanLine.replace(/^data:\s*/, "");
if (dataStr === "[DONE]") break;
try {
const parsed = JSON.parse(dataStr);
// Cross-provider safe fallback text extractor
const chunk = parsed.token?.text || parsed.choices?.[0]?.delta?.content || parsed.generated_text || "";
UI.onTextChunk(chunk);
} catch (e) {}
}
}
}
UI.onStatus("Stream Complete");
} catch (err) {
UI.onError(err.message);
}
}
/**
* Handles Space Failovers with Dynamic Endpoint Schema Parsing
*/
async runVideo(prompt, UI, poolIndex = 0) {
if (poolIndex >= CONFIG.videoSpaces.length) {
UI.onError("All public ZeroGPU Space pools are saturated. Please try again shortly.");
return;
}
const currentSpace = CONFIG.videoSpaces[poolIndex];
UI.onStatus(`[Pool ${poolIndex + 1}/${CONFIG.videoSpaces.length}] Testing payload match: ${currentSpace}...`);
try {
const { Client } = await import("https://cdn.jsdelivr.net/npm/@gradio/client/+esm");
const app = await Client.connect(currentSpace, { hf_token: this.token || undefined });
UI.onStatus("Bypassing queues... waiting for hardware allocation context...");
// Dynamic scan of endpoint configuration array schema to ensure parameter match
const predictEndpoint = app.config.api_info.named_endpoints["/predict"] || app.config.api_info.unnamed_endpoints["0"];
const expectedInputLength = predictEndpoint.parameters.length;
// Create safely structured payload with array matching exactly what the Space expects
const payloadArray = Array(expectedInputLength).fill(null);
payloadArray[0] = prompt; // Prompt is always index 0
if (expectedInputLength > 1) {
payloadArray[1] = Math.floor(Math.random() * 100000); // Index 1 is standard for Seed integers
}
const result = await app.predict(predictEndpoint.endpoint || "/predict", payloadArray);
if (result?.data?.[0]) {
const rawData = result.data[0];
const videoUrl = typeof rawData === 'object' ? rawData.url : rawData;
UI.onVideo(videoUrl);
} else {
throw new Error("Payload mismatch on dynamic endpoint.");
}
} catch (error) {
console.warn(`Space [${currentSpace}] failed processing. Cascading execution downward...`, error);
await this.runVideo(prompt, UI, poolIndex + 1);
}
}
}
// Initialize UI Elements & Control Logic
const engine = new ProductionHFEngine();
const nodes = {
selector: document.getElementById("model-selector"),
prompt: document.getElementById("prompt-input"),
btn: document.getElementById("generate-btn"),
status: document.getElementById("status-indicator"),
authStatus: document.getElementById("auth-status"),
loginBtn: document.getElementById("login-btn"),
text: document.getElementById("text-output"),
img: document.getElementById("image-output"),
video: document.getElementById("video-output"),
placeholder: document.getElementById("placeholder-view")
};
// UI View State Orchestrator
const activeView = (target) => {
[nodes.text, nodes.img, nodes.video, nodes.placeholder].forEach(n => n.classList.add("hidden"));
target.classList.remove("hidden");
};
// Explicit Token Authentication Scraper & Cleanup Loop
const processOAuthCallback = () => {
// Check implicit hash parameter context matching Hugging Face's identity delivery
const hashParams = new URLSearchParams(window.location.hash.substring(1));
const tokenFromUrl = hashParams.get("access_token");
if (tokenFromUrl) {
engine.setToken(tokenFromUrl);
// Wipe credentials immediately from the visible history context
window.history.replaceState({}, document.title, window.location.pathname);
}
if (engine.token) {
nodes.authStatus.innerText = "Authenticated via User Token Slot";
nodes.authStatus.classList.replace("text-gray-400", "text-emerald-400");
nodes.loginBtn.innerText = "Disconnect";
}
};
nodes.loginBtn.addEventListener("click", () => {
if (engine.token) {
engine.setToken("");
window.location.reload();
} else {
// Point straight to the implicit OAuth flow engine built inside Space containers
const hfSpaceId = window.location.host.split('.')[0].replace('-static', '');
// Correct implicit redirect uri format matching Hugging Face standard scopes
window.location.href = `https://huggingface.co/oauth/authorize?client_id=${hfSpaceId}&response_type=token&scope=openid%20inference-api`;
}
});
// Click Controller Event Trigger
nodes.btn.addEventListener("click", async () => {
const model = nodes.selector.value;
const prompt = nodes.prompt.value.trim();
if (!prompt) return alert("Please provide an action input prompt first.");
nodes.btn.disabled = true;
nodes.btn.classList.add("opacity-50");
const UIHandlers = {
onStatus: (msg) => { nodes.status.innerText = msg; },
onError: (err) => {
nodes.status.innerText = "Error encountered";
activeView(nodes.text);
nodes.text.innerText = `⚠️ Execution halted:\n\n${err}`;
nodes.btn.disabled = false;
nodes.btn.classList.remove("opacity-50");
},
onTextStart: () => {
activeView(nodes.text);
nodes.text.innerText = "";
},
onTextChunk: (chunk) => {
nodes.text.innerText += chunk;
nodes.text.scrollTop = nodes.text.scrollHeight;
},
onImage: (url) => {
activeView(nodes.img);
nodes.img.src = url;
nodes.status.innerText = "Success";
nodes.btn.disabled = false;
nodes.btn.classList.remove("opacity-50");
},
onVideo: (url) => {
activeView(nodes.video);
nodes.video.src = url;
nodes.video.play();
nodes.status.innerText = "Success";
nodes.btn.disabled = false;
nodes.btn.classList.remove("opacity-50");
}
};
if (model === "VIDEO_POOL") {
await engine.runVideo(prompt, UIHandlers);
} else {
await engine.runInference(model, prompt, UIHandlers);
nodes.btn.disabled = false;
nodes.btn.classList.remove("opacity-50");
}
});
// Bootstrap on Window Load Context
processOAuthCallback();
</script>
</body>
</html>