Spaces:
Running
Running
| // script.js β Shared logic | |
| // Utilities | |
| const $ = (sel) => document.querySelector(sel); | |
| const $$ = (sel) => [...document.querySelectorAll(sel)]; | |
| // Router | |
| function showPage(id) { | |
| $$('.page').forEach(p => p.classList.add('hidden')); | |
| $(`#${id}`).classList.remove('hidden'); | |
| } | |
| window.showPage = showPage; | |
| // Mock Model Registry | |
| export const MODELS = [ | |
| { | |
| id: 1, | |
| name: "HuggingFace BERT", | |
| type: "language", | |
| description: "Pre-trained transformer for NLP tasks like classification or Q&A.", | |
| endpoint: "https://api.mock/bert", | |
| tags: ["text", "classification", "embedding", "transformer"] | |
| }, | |
| { | |
| id: 2, | |
| name: "Facebook Prophet", | |
| type: "time-series", | |
| description: "Forecasting tool for seasonal and trend time-series data.", | |
| endpoint: "https://api.mock/prophet", | |
| tags: ["forecast", "timeseries", "seasonality", "trend"] | |
| }, | |
| { | |
| id: 3, | |
| name: "YOLOv8", | |
| type: "vision", | |
| description: "State-of-the-art object detection and segmentation model.", | |
| endpoint: "https://api.mock/yolo", | |
| tags: ["image", "detection", "objects", "realtime"] | |
| }, | |
| { | |
| id: 4, | |
| name: "Stable Diffusion", | |
| type: "generative", | |
| description: "Generate high-quality images from text prompts.", | |
| endpoint: "https://api.mock/sd", | |
| tags: ["image", "generation", "text2img", "diffusion"] | |
| } | |
| ]; | |
| // Knowledge Graph | |
| export const TASK_KEYWORDS = { | |
| "forecast": ["time-series"], | |
| "sales": ["time-series"], | |
| "image": ["vision", "generative"], | |
| "generate": ["generative"], | |
| "text": ["language"], | |
| "detect": ["vision"], | |
| "report": ["language"], | |
| "trend": ["time-series"] | |
| }; | |
| // Simple state | |
| window.workflowModels = []; | |
| // Render Marketplace | |
| function renderModels(filter = "") { | |
| const grid = $("#model-grid"); | |
| grid.innerHTML = ""; | |
| const typeFilter = $(".tag-btn.active")?.dataset.type || ""; | |
| MODELS.filter(m => { | |
| if (typeFilter && m.type !== typeFilter) return false; | |
| if (filter && !m.name.toLowerCase().includes(filter.toLowerCase()) && !m.tags.some(t => t.toLowerCase().includes(filter.toLowerCase()))) return false; | |
| return true; | |
| }).forEach(m => { | |
| const card = document.createElement("model-card"); | |
| card.setAttribute("data-id", m.id); | |
| card.setAttribute("data-name", m.name); | |
| card.setAttribute("data-type", m.type); | |
| card.setAttribute("data-desc", m.description); | |
| card.setAttribute("data-tags", JSON.stringify(m.tags)); | |
| grid.appendChild(card); | |
| }); | |
| feather.replace(); | |
| } | |
| window.renderModels = renderModels; | |
| // Planner Agent | |
| function planWorkflow(task) { | |
| const logs = []; | |
| logs.push(`π Planner received task: "${task}"`); | |
| const words = task.toLowerCase().split(/\W+/); | |
| const matchedTypes = new Set(); | |
| words.forEach(w => { | |
| if (TASK_KEYWORDS[w]) { | |
| TASK_KEYWORDS[w].forEach(t => matchedTypes.add(t)); | |
| } | |
| }); | |
| logs.push(`π Matched types: ${[...matchedTypes].join(", ")}`); | |
| const suggested = MODELS.filter(m => matchedTypes.has(m.type)); | |
| logs.push(`β Suggested models: ${suggested.map(m => m.name).join(", ")}`); | |
| const panel = $("#workflow-suggestion"); | |
| panel.innerHTML = `<div class="mb-4"><strong>Suggested Workflow:</strong></div> | |
| <div class="flex flex-col gap-3"> | |
| ${suggested.map(m => ` | |
| <div class="p-3 rounded-lg bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 flex items-center justify-between"> | |
| <div> | |
| <div class="font-semibold">${m.name}</div> | |
| <div class="text-sm text-gray-500 dark:text-gray-400">${m.description}</div> | |
| </div> | |
| <button class="px-3 py-1 rounded-md bg-indigo-600 hover:bg-indigo-700 text-white text-sm add-to-workflow-btn" data-id="${m.id}">Add</button> | |
| </div> | |
| `).join("")} | |
| </div>`; | |
| // Log Panel Update | |
| const logPanel = document.querySelector("log-panel"); | |
| if (logPanel) logPanel.logs = logs; | |
| // Bind add buttons | |
| $$(".add-to-workflow-btn").forEach(btn => { | |
| btn.onclick = () => { | |
| const id = Number(btn.dataset.id); | |
| if (!window.workflowModels.find(m => m.id === id)) { | |
| window.workflowModels.push(MODELS.find(m => m.id === id)); | |
| renderWorkflowStudio(); | |
| } | |
| }; | |
| }); | |
| } | |
| // Workflow Studio | |
| function renderWorkflowStudio() { | |
| const studio = $("#workflow-studio"); | |
| if (!window.workflowModels.length) { | |
| studio.innerHTML = `<div class="text-gray-500 dark:text-gray-400 text-center py-10">No models added yet. Visit Marketplace or Agents to add.</div>`; | |
| return; | |
| } | |
| studio.innerHTML = ` | |
| <div id="workflow-list" class="mb-4 flex flex-col gap-3"> | |
| ${window.workflowModels.map((m, idx) => ` | |
| <div class="p-4 rounded-lg bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 flex items-center justify-between cursor-move" draggable="true" data-index="${idx}"> | |
| <div class="flex items-center gap-3"> | |
| <i data-feather="menu" class="w-4 h-4 text-gray-400"></i> | |
| <div> | |
| <div class="font-semibold">${m.name}</div> | |
| <div class="text-sm text-gray-500 dark:text-gray-400">${m.type} β’ ${m.description}</div> | |
| </div> | |
| </div> | |
| <button class="px-3 py-1 rounded-md bg-red-500 hover:bg-red-600 text-white text-sm remove-model-btn" data-index="${idx}">Remove</button> | |
| </div> | |
| `).join("")} | |
| </div> | |
| <button id="run-workflow-btn" class="px-4 py-2 rounded-lg bg-green-600 hover:bg-green-700 text-white font-medium">Run Workflow</button> | |
| <div id="workflow-results" class="mt-6"></div> | |
| `; | |
| feather.replace(); | |
| bindReorder(); | |
| $$(".remove-model-btn").forEach(btn => { | |
| btn.onclick = () => { | |
| const idx = Number(btn.dataset.index); | |
| window.workflowModels.splice(idx, 1); | |
| renderWorkflowStudio(); | |
| }; | |
| }; | |
| $("#run-workflow-btn").onclick = runWorkflow; | |
| } | |
| // Drag Reorder | |
| function bindReorder() { | |
| const list = $("#workflow-list"); | |
| let dragged = null; | |
| list.querySelectorAll("[draggable=true]").forEach(el => { | |
| el.ondragstart = (e) => { dragged = e.target.closest("[data-index]"); }; | |
| el.ondragover = (e) => e.preventDefault(); | |
| el.ondrop = (e) => { | |
| e.preventDefault(); | |
| const target = e.target.closest("[data-index]"); | |
| if (!target || target === dragged) return; | |
| const from = Number(dragged.dataset.index); | |
| const to = Number(target.dataset.index); | |
| const temp = window.workflowModels[from]; | |
| window.workflowModels[from] = window.workflowModels[to]; | |
| window.workflowModels[to] = temp; | |
| renderWorkflowStudio(); | |
| }; | |
| }); | |
| } | |
| // Run Workflow | |
| async function runWorkflow() { | |
| const results = $("#workflow-results"); | |
| results.innerHTML = `<div class="text-gray-500 dark:text-gray-400">Running...</div>`; | |
| const logs = []; | |
| const outputs = []; | |
| for (const m of window.workflowModels) { | |
| logs.push(`π Calling ${m.name} at ${m.endpoint}`); | |
| await new Promise(r => setTimeout(r, 600)); // mock latency | |
| let out = `[Mock Output] ${m.name}: `; | |
| if (m.type === "language") out += "Generated text summary."; | |
| if (m.type === "time-series") out += "Sales forecast chart data."; | |
| if (m.type === "vision") out += "Detected 5 objects."; | |
| if (m.type === "generative") out += `<img src="https://static.photos/abstract/640x360/${m.id}" alt="generated image" class="rounded-lg mt-2 max-w-full"/>`; | |
| outputs.push(out); | |
| logs.push(`β ${m.name} responded`); | |
| } | |
| logs.push("β¨ Workflow complete"); | |
| const logPanel = document.querySelector("log-panel"); | |
| if (logPanel) logPanel.logs = logs; | |
| results.innerHTML = ` | |
| <div class="mb-2 font-semibold">Results:</div> | |
| <div class="flex flex-col gap-3"> | |
| ${outputs.map(o => `<div class="p-3 rounded-lg bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700">${o}</div>`).join("")} | |
| </div> | |
| `; | |
| } | |
| // Init | |
| document.addEventListener("DOMContentLoaded", () => { | |
| renderModels(); | |
| // Search | |
| $("#search-input").addEventListener("input", e => renderModels(e.target.value)); | |
| // Type Filter | |
| $(".tag-btn").forEach(btn => { | |
| btn.onclick = () => { | |
| $(".tag-btn").forEach(b => b.classList.remove("active")); | |
| btn.classList.add("active"); | |
| renderModels(); | |
| }; | |
| }); | |
| // Planner | |
| $("#plan-btn").onclick = () => { | |
| const task = $("#task-input").value.trim(); | |
| if (!task) return; | |
| planWorkflow(task); | |
| }; | |
| // Workflow Studio | |
| window.renderWorkflowStudio = renderWorkflowStudio; | |
| renderWorkflowStudio(); | |
| }); | |