add ModelManager component and infrastructure to support local model management, downloading, and server integration

This commit is contained in:
techjarves committed 2026-06-07 10:10:53 +05:30
1 parent 6595213696
commit 992a642b40
10 files changed
+207 -56

No files matched your search

+1 -5
View File
@@ -1,5 +1,5 @@
import React, { memo, useState, useRef, useCallback } from "react";
import { Sparkles, Download, Copy, RefreshCw, Check, Sliders, Layers, Trash2 } from "lucide-react";
import { Sparkles, Download, Copy, RefreshCw, Check, Sliders, Trash2 } from "lucide-react";
import { generateImage, startServer, stopServer, waitForServerReady, getBackendStatus, getGenerationProgress, saveGeneratedOutput, deleteGeneratedOutputs } from "../services/api";
const GalleryItem = memo(({ img, idx, isSelected, onClick }) => {
@@ -600,10 +600,6 @@ function Generator({
<RefreshCw size={14} />
<span>Steps: {constraints.steps}</span>
</div>
<div className="status-chip" onClick={() => setActiveTab("constraints")}>
<Layers size={14} />
<span>CFG: {constraints.cfgScale.toFixed(1)}</span>
</div>
<div className="status-chip" onClick={() => setActiveTab("constraints")}>
<Sparkles size={14} />
<span>Sampler: {constraints.sampler}</span>
@@ -209,26 +209,6 @@ function ImageConstraints({ constraints, setConstraints, activeModel, specs, bac
</span>
</div>
{/* CFG Scale Slider */}
<div className="m3-slider-group" style={{ marginTop: "8px" }}>
<div className="m3-slider-header">
<span className="m3-slider-label">Prompt Strictness (Instruction obedience)</span>
<span className="m3-slider-value">{constraints.cfgScale.toFixed(1)}</span>
</div>
<input
type="range"
className="m3-slider"
value={constraints.cfgScale}
onChange={(e) => updateConstraint("cfgScale", parseFloat(e.target.value))}
min="1"
max="15"
step="0.5"
/>
<span style={{ fontSize: "0.75rem", color: "var(--md-sys-color-outline)", lineHeight: "1.3" }}>
How closely the AI follows your text prompt. Lower = more creative. Higher = more strict.
</span>
</div>
{/* Random Seed DNA */}
<div className="m3-text-field" style={{ marginTop: "8px" }}>
<label className="m3-text-field-label">Image Blueprint (DNA Seed)</label>
+5 -3
View File
@@ -250,10 +250,12 @@ function ModelManager({ activeModel, setActiveModel, serverRunning, setServerRun
const performLoadModel = async (modelId) => {
const isTauriDesktop = typeof window !== "undefined" && window.__TAURI_INTERNALS__ !== undefined;
const isLocalServerMode = typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1");
const backendStatus = await getBackendStatus();
const backendPort = backendStatus?.port || 8080;
if (!isTauriDesktop && !isLocalServerMode) {
const isAlreadyRunning = await pingServer();
const cmd = `.\\backend\\win\\vulkan\\sd-vulkan.exe --listen-port 8080 --model .\\models\\${modelId} --threads 8`;
const cmd = `.\\backend\\win\\vulkan\\sd-vulkan.exe --listen-port ${backendPort} --model .\\models\\${modelId} --threads 8`;
if (isAlreadyRunning) {
if (await showConfirm({ title: "Web Browser Mode", message: `To load "${modelId}" on the active GPU server, restart your terminal backend with:\n\n${cmd}\n\nUpdate the UI status anyway?`, confirmLabel: "Update UI" })) {
@@ -261,7 +263,7 @@ function ModelManager({ activeModel, setActiveModel, serverRunning, setServerRun
setServerRunning(true);
}
} else {
if (await showConfirm({ title: "Backend Not Running", message: `C++ backend server is not running on port 8080.\n\nTo run locally, start:\n\n${cmd}\n\nProceed in Simulation Mode instead?`, confirmLabel: "Proceed" })) {
if (await showConfirm({ title: "Backend Not Running", message: `C++ backend server is not running on port ${backendPort}.\n\nTo run locally, start:\n\n${cmd}\n\nProceed in Simulation Mode instead?`, confirmLabel: "Proceed" })) {
setActiveModel(modelId);
setServerRunning(false);
}
@@ -330,7 +332,7 @@ function ModelManager({ activeModel, setActiveModel, serverRunning, setServerRun
setActiveModel(modelId);
setServerRunning(true);
} else {
throw new Error(crashError || "Model server failed to respond on port 8080.");
throw new Error(crashError || `Model server failed to respond on port ${backendPort}.`);
}
} catch (e) {
console.error("Failed to load model:", e);
+27 -5
View File
@@ -8,12 +8,32 @@ export const isTauri = () => {
// Cached hardware specs
let cachedSpecs = null;
let cachedBackendPort = null;
export const EXPECTED_SERVER_BUILD = "polish-setup-v1";
const isLocalServerMode = () => {
return typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1");
};
async function getBackendPort() {
if (isLocalServerMode()) {
try {
const status = await getBackendStatus();
const port = Number(status?.port);
if (Number.isInteger(port) && port > 0) {
cachedBackendPort = port;
return port;
}
} catch (_) {}
}
if (cachedBackendPort) return cachedBackendPort;
return 8080;
}
async function getBackendBaseUrl() {
return `http://127.0.0.1:${await getBackendPort()}`;
}
export function formatBytes(bytes) {
const value = Number(bytes) || 0;
if (value <= 0) return "0 B";
@@ -208,10 +228,11 @@ export async function listLocalModels() {
// In web/portable mode this calls serve.cjs management API which restarts the
// sd-vulkan.exe process with --steps, --cfg-scale, --sampling-method flags.
export async function startServer(modelPath, constraints) {
const backendPort = await getBackendPort();
if (isTauri()) {
const launchParams = {
model_path: modelPath,
port: 8080,
port: backendPort,
use_gpu: constraints.useGpu !== false,
backend_type: constraints.backendType || (constraints.useGpu === false ? "cpu" : "auto"),
threads: constraints.threads || 8,
@@ -242,6 +263,9 @@ export async function startServer(modelPath, constraints) {
if (!res.ok || data.ok === false) {
throw new Error(data.error || `Backend restart failed (HTTP ${res.status})`);
}
if (Number.isInteger(Number(data.port)) && Number(data.port) > 0) {
cachedBackendPort = Number(data.port);
}
console.log("Backend restart:", data.message);
return data.message;
} catch (e) {
@@ -320,8 +344,7 @@ export async function generateImage(prompt, negativePrompt, constraints, activeM
console.log("Initiating image generation:", { prompt, negativePrompt, constraints, activeModelName });
const startTime = Date.now();
const port = 8080;
const baseUrl = `http://127.0.0.1:${port}`;
const baseUrl = await getBackendBaseUrl();
// Prepare payload based on standard stable-diffusion.cpp REST endpoint schemas
const payload = {
@@ -738,8 +761,7 @@ export async function deleteModel(filename) {
// Ping the server to check if it is active and responding
export async function pingServer() {
const port = 8080;
const baseUrl = `http://127.0.0.1:${port}`;
const baseUrl = await getBackendBaseUrl();
try {
const response = await fetch(`${baseUrl}/v1/models`, { method: "GET" });
return response.ok;