Initial ForgeLM import
This commit is contained in:
commit
a392b9dc60
4296 files changed
+1212675
No files matched your search
@@ -0,0 +1,311 @@
|
||||
import { useRuntimeManager } from "../useRuntimeManager";
|
||||
import { getRuntimeHealthSummary, getRuntimeWorkspaceSummary } from "../runtime-utils";
|
||||
import type { RuntimeConfig, RuntimeDraftFieldKey, RuntimeDraftFieldMeta, RuntimeDraftFieldState, RuntimeSnapshot, RuntimeWorkspaceHealth, RuntimeWorkspaceSummary } from "../types";
|
||||
import "./RuntimeWorkspace.css";
|
||||
|
||||
interface ExternalRuntimeProps {
|
||||
snapshot: RuntimeSnapshot;
|
||||
savedBaseline: RuntimeConfig;
|
||||
draftConfig: RuntimeConfig;
|
||||
draftDirty: boolean;
|
||||
draftOpen?: boolean;
|
||||
draftFields: RuntimeDraftFieldState;
|
||||
loading?: boolean;
|
||||
busy?: boolean;
|
||||
activeLifecycleCommand?: RuntimeSnapshot["active_command"];
|
||||
lastLifecycleResult?: RuntimeSnapshot["last_command_result"];
|
||||
lifecycleBusy?: boolean;
|
||||
applyImpact?: "immediate" | "restart_required" | "save_before_apply";
|
||||
openDraft?: () => void;
|
||||
closeDraft?: () => void;
|
||||
resetDraft?: () => void;
|
||||
updateDraft?: (patch: Partial<RuntimeConfig>) => void;
|
||||
updateDraftField?: <K extends RuntimeDraftFieldKey>(field: K, value: RuntimeConfig[K]) => void;
|
||||
updateDraftFieldMeta?: (field: RuntimeDraftFieldKey, patch: Partial<RuntimeDraftFieldMeta>) => void;
|
||||
saveConfig?: (patch: Partial<RuntimeConfig>) => Promise<RuntimeSnapshot>;
|
||||
startLocal?: () => Promise<unknown>;
|
||||
stopLocal?: () => Promise<unknown>;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
runtime?: ExternalRuntimeProps;
|
||||
}
|
||||
|
||||
function summarizePath(path: string | null): string {
|
||||
if (!path) return "No model selected";
|
||||
const segments = path.split(/[\\/]/).filter(Boolean);
|
||||
return segments[segments.length - 1] || path;
|
||||
}
|
||||
|
||||
function getLifecycleActionLabel(command: string): string | null {
|
||||
switch (command) {
|
||||
case "start":
|
||||
return "Starting local API...";
|
||||
case "stop":
|
||||
return "Stopping local API...";
|
||||
case "save":
|
||||
return "Saving changes...";
|
||||
case "save_and_start":
|
||||
return "Saving and starting local API...";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default function RuntimeWorkspace({ runtime: externalRuntime }: Props) {
|
||||
const internalManager = useRuntimeManager();
|
||||
|
||||
// Use external runtime props if provided, otherwise use internal manager
|
||||
const useExternal = !!externalRuntime;
|
||||
const runtime = useExternal ? externalRuntime : internalManager;
|
||||
|
||||
// Compute workspace summary and health from snapshot when using external props
|
||||
const workspaceSummary: RuntimeWorkspaceSummary = useExternal
|
||||
? getRuntimeWorkspaceSummary(runtime.snapshot)
|
||||
: internalManager.workspaceSummary;
|
||||
|
||||
const workspaceHealth: RuntimeWorkspaceHealth = useExternal
|
||||
? getRuntimeHealthSummary(runtime.snapshot)
|
||||
: internalManager.workspaceHealth;
|
||||
|
||||
const {
|
||||
draftDirty,
|
||||
draftConfig,
|
||||
savedBaseline,
|
||||
draftFields,
|
||||
applyImpact,
|
||||
busy,
|
||||
activeLifecycleCommand,
|
||||
lastLifecycleResult,
|
||||
lifecycleBusy,
|
||||
loading,
|
||||
openDraft,
|
||||
closeDraft,
|
||||
resetDraft,
|
||||
updateDraft,
|
||||
updateDraftField,
|
||||
updateDraftFieldMeta,
|
||||
saveConfig,
|
||||
startLocal,
|
||||
stopLocal,
|
||||
snapshot,
|
||||
} = runtime;
|
||||
|
||||
const isLocal = snapshot.config.connection_mode === "local";
|
||||
const lifecycleResult = lastLifecycleResult ?? { command: "idle" as const, outcome: "succeeded" as const, message: "" };
|
||||
const showLifecycleResult = lifecycleResult.command !== "idle" && !lifecycleBusy;
|
||||
const lifecycleLabel = getLifecycleActionLabel(activeLifecycleCommand ?? "");
|
||||
|
||||
const hasInvalidDraft = Object.values(draftFields).some((meta) => meta?.invalid);
|
||||
const hasPendingDraft = Object.values(draftFields).some((meta) => meta?.pending);
|
||||
const hasBlockingDraftState = hasInvalidDraft || hasPendingDraft;
|
||||
const hasPendingModeChange = draftConfig.connection_mode !== savedBaseline.connection_mode;
|
||||
const restartRequiredWhileRunning =
|
||||
draftDirty && draftConfig.connection_mode === "local" && snapshot.process_running && applyImpact === "restart_required";
|
||||
|
||||
const applyGuidance = !draftDirty || draftConfig.connection_mode !== "local"
|
||||
? null
|
||||
: hasPendingModeChange || applyImpact === "save_before_apply"
|
||||
? "Stop the local API or save first before applying this change."
|
||||
: applyImpact === "restart_required"
|
||||
? "Restart required to apply saved changes."
|
||||
: "Applies immediately after save.";
|
||||
|
||||
const lifecycleResultClass =
|
||||
lifecycleResult.outcome === "failed"
|
||||
? "runtime-workspace-result-danger"
|
||||
: lifecycleResult.outcome === "blocked"
|
||||
? "runtime-workspace-result-warning"
|
||||
: "runtime-workspace-result-success";
|
||||
|
||||
const healthToneClass = `runtime-workspace-health-${workspaceHealth.tone}`;
|
||||
|
||||
const saveLabel = activeLifecycleCommand === "save" ? "Saving changes..." : "Save changes";
|
||||
const tertiaryLabel = hasPendingModeChange
|
||||
? "Save mode before applying"
|
||||
: activeLifecycleCommand === "start"
|
||||
? "Starting local API..."
|
||||
: activeLifecycleCommand === "stop"
|
||||
? "Stopping local API..."
|
||||
: restartRequiredWhileRunning
|
||||
? "Restart required to apply saved changes"
|
||||
: snapshot.process_running
|
||||
? "Stop local API"
|
||||
: "Save and start local API";
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<section className="runtime-workspace" aria-label="Runtime workspace">
|
||||
<div className="runtime-workspace-loading">Loading runtime...</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="runtime-workspace" aria-label="Runtime workspace">
|
||||
{/* Header / Summary Section */}
|
||||
<div className="runtime-workspace-header">
|
||||
<div className="runtime-workspace-header-top">
|
||||
<div className="runtime-workspace-kicker">Runtime workspace</div>
|
||||
<span className={`runtime-workspace-mode runtime-workspace-mode-${workspaceSummary.modeLabel}`}>
|
||||
{workspaceSummary.modeLabel === "local" ? "Local" : "Remote"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="runtime-workspace-status">{workspaceSummary.statusLabel}</div>
|
||||
</div>
|
||||
|
||||
{/* Health Section */}
|
||||
<div className="runtime-workspace-health-section">
|
||||
<div className="runtime-workspace-section-label">Runtime health</div>
|
||||
<div className={`runtime-workspace-health ${healthToneClass}`}>
|
||||
<div className="runtime-workspace-health-headline">{workspaceHealth.headline}</div>
|
||||
{workspaceHealth.detail && <div className="runtime-workspace-health-detail">{workspaceHealth.detail}</div>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Connection Details */}
|
||||
<div className="runtime-workspace-details">
|
||||
<div className="runtime-workspace-detail-row">
|
||||
<span className="runtime-workspace-detail-label">Endpoint</span>
|
||||
<span className="runtime-workspace-detail-value">{workspaceSummary.endpointLabel || "Not configured"}</span>
|
||||
</div>
|
||||
<div className="runtime-workspace-detail-row">
|
||||
<span className="runtime-workspace-detail-label">Model</span>
|
||||
<span className="runtime-workspace-detail-value">{workspaceSummary.modelLabel || "No model selected"}</span>
|
||||
</div>
|
||||
{isLocal && (
|
||||
<div className="runtime-workspace-detail-row">
|
||||
<span className="runtime-workspace-detail-label">Start policy</span>
|
||||
<span className="runtime-workspace-detail-value">
|
||||
{snapshot.config.start_policy === "on_app_launch" ? "Auto-start" : "Manual"}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Lifecycle Result Banner */}
|
||||
{showLifecycleResult && (
|
||||
<div className={`runtime-workspace-result ${lifecycleResultClass}`} role="status" aria-live="polite">
|
||||
{lifecycleResult.message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Actions */}
|
||||
<div className="runtime-workspace-actions">
|
||||
<button className="runtime-workspace-btn runtime-workspace-btn-secondary" onClick={openDraft ?? (() => {})} type="button" disabled={lifecycleBusy}>
|
||||
{draftDirty ? "Edit draft" : "Manage"}
|
||||
</button>
|
||||
{isLocal && (
|
||||
<>
|
||||
<button
|
||||
className="runtime-workspace-btn runtime-workspace-btn-primary"
|
||||
onClick={snapshot.process_running ? (stopLocal ?? (() => {})) : (startLocal ?? (() => {}))}
|
||||
type="button"
|
||||
disabled={busy || lifecycleBusy || (snapshot.process_running && snapshot.status === "stopping")}
|
||||
>
|
||||
{lifecycleLabel ?? (snapshot.process_running ? "Stop" : "Start")}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Draft Settings (inline when open) */}
|
||||
{draftDirty && (
|
||||
<div className="runtime-workspace-draft-panel">
|
||||
<div className="runtime-workspace-draft-header">
|
||||
<span className="runtime-workspace-draft-title">Draft settings</span>
|
||||
<span className="runtime-workspace-draft-badge">Unsaved changes</span>
|
||||
</div>
|
||||
|
||||
{/* Apply Guidance */}
|
||||
{applyGuidance && (
|
||||
<div className={`runtime-workspace-apply-guidance${applyImpact !== "immediate" ? " is-warning" : ""}`}>
|
||||
{applyGuidance}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Local mode draft fields */}
|
||||
{draftConfig.connection_mode === "local" && updateDraft && (
|
||||
<>
|
||||
<div className="runtime-workspace-draft-field">
|
||||
<label className="runtime-workspace-draft-label">Models folder</label>
|
||||
<input
|
||||
className="runtime-workspace-draft-input"
|
||||
value={draftConfig.models_dir || ""}
|
||||
onChange={(e) => updateDraft({ models_dir: e.target.value })}
|
||||
placeholder="D:/models"
|
||||
/>
|
||||
</div>
|
||||
<div className="runtime-workspace-draft-field">
|
||||
<label className="runtime-workspace-draft-label">Selected model</label>
|
||||
<input
|
||||
className="runtime-workspace-draft-input"
|
||||
value={draftConfig.model_path || ""}
|
||||
onChange={(e) => updateDraft({ model_path: e.target.value || null })}
|
||||
placeholder="D:/models/model.gguf"
|
||||
/>
|
||||
</div>
|
||||
<div className="runtime-workspace-draft-field">
|
||||
<label className="runtime-workspace-draft-label">GPU layers</label>
|
||||
<input
|
||||
className="runtime-workspace-draft-input"
|
||||
type="number"
|
||||
min={0}
|
||||
value={draftConfig.gpu_layers}
|
||||
onChange={(e) => updateDraft({ gpu_layers: parseInt(e.target.value, 10) || 0 })}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Remote mode draft fields */}
|
||||
{draftConfig.connection_mode === "remote" && updateDraft && (
|
||||
<>
|
||||
<div className="runtime-workspace-draft-field">
|
||||
<label className="runtime-workspace-draft-label">Server URL</label>
|
||||
<input
|
||||
className="runtime-workspace-draft-input"
|
||||
value={draftConfig.server_url}
|
||||
onChange={(e) => updateDraft({ server_url: e.target.value })}
|
||||
placeholder="https://api.example.com"
|
||||
/>
|
||||
</div>
|
||||
<div className="runtime-workspace-draft-field">
|
||||
<label className="runtime-workspace-draft-label">API key</label>
|
||||
<input
|
||||
className="runtime-workspace-draft-input"
|
||||
type="password"
|
||||
value={draftConfig.api_key}
|
||||
onChange={(e) => updateDraft({ api_key: e.target.value })}
|
||||
placeholder="sk-..."
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Draft Actions */}
|
||||
{updateDraft && resetDraft && (
|
||||
<div className="runtime-workspace-draft-actions">
|
||||
<button
|
||||
className="runtime-workspace-btn runtime-workspace-btn-secondary"
|
||||
onClick={resetDraft}
|
||||
type="button"
|
||||
disabled={busy || lifecycleBusy}
|
||||
>
|
||||
Discard
|
||||
</button>
|
||||
<button
|
||||
className="runtime-workspace-btn runtime-workspace-btn-primary"
|
||||
onClick={() => { void saveConfig?.(draftConfig); }}
|
||||
type="button"
|
||||
disabled={busy || lifecycleBusy || hasBlockingDraftState}
|
||||
>
|
||||
{saveLabel}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user