Initial ForgeLM import

This commit is contained in:
Evandro Fonseca Junior committed 2026-03-29 19:10:57 -03:00
commit a392b9dc60
4296 files changed
+1212675

No files matched your search

@@ -0,0 +1,541 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { bootstrapRuntime, getRuntimeSnapshot, saveRuntimeConfig, scanRuntimeModels, startRuntime, stopRuntime } from "./api";
import {
createDefaultRuntimeSnapshot,
createRuntimeLifecycleResult,
getRuntimeApplyImpact,
getRuntimeConnection,
getRuntimeHealthSummary,
getRuntimeWorkspaceSummary,
validateModelFile,
validateModelsDirectory,
validateSourceMode,
} from "./runtime-utils";
import { createRuntimeDraftFieldState, createRuntimeDraftState } from "./types";
import type {
RuntimeApplyImpact,
RuntimeConfig,
RuntimeDraftFieldKey,
RuntimeDraftFieldMeta,
RuntimeDraftState,
RuntimeLifecycleCommand,
RuntimeLifecycleResult,
RuntimeModelFile,
RuntimeSnapshot,
RuntimeWorkspaceHealth,
RuntimeWorkspaceSummary,
} from "./types";
function areConfigsEqual(left: RuntimeConfig, right: RuntimeConfig): boolean {
return JSON.stringify(left) === JSON.stringify(right);
}
function cloneConfig(config: RuntimeConfig): RuntimeConfig {
return {
...config,
extra_model_paths: [...config.extra_model_paths],
remote_presets: config.remote_presets.map((preset) => ({ ...preset })),
};
}
function applyOperationalSnapshot(current: RuntimeSnapshot, next: RuntimeSnapshot): RuntimeSnapshot {
return {
...current,
status: next.status,
ready: next.ready,
process_running: next.process_running,
resolved_url: next.resolved_url,
binary_path: next.binary_path,
last_error: next.last_error,
active_command: next.active_command,
last_command_result: next.last_command_result,
};
}
function normalizeSnapshot(snapshot: RuntimeSnapshot): RuntimeSnapshot {
return {
...createDefaultRuntimeSnapshot(),
...snapshot,
config: {
...createDefaultRuntimeSnapshot().config,
...snapshot.config,
},
last_command_result:
snapshot.last_command_result ?? createRuntimeLifecycleResult(snapshot.active_command ?? "idle"),
active_command: snapshot.active_command ?? "idle",
};
}
function reconcileDraftState(current: RuntimeDraftState, next: RuntimeSnapshot): RuntimeDraftState {
const normalizedNext = normalizeSnapshot(next);
if (current.isDirty) {
return {
...current,
liveSnapshot: applyOperationalSnapshot(current.liveSnapshot, normalizedNext),
};
}
// Run source-mode validation when reconciling to baseline
const validation = validateSourceMode(normalizedNext.config);
const updatedFields = createRuntimeDraftFieldState();
for (const [fieldName, fieldValidation] of Object.entries(validation.fieldValidations)) {
updatedFields[fieldName as RuntimeDraftFieldKey] = {
invalid: !fieldValidation.valid,
pending: false,
message: fieldValidation.message,
};
}
return {
...current,
liveSnapshot: normalizedNext,
savedBaseline: cloneConfig(normalizedNext.config),
draftConfig: cloneConfig(normalizedNext.config),
fields: updatedFields,
// Preserve scanned models state during reconciliation
scannedModels: current.scannedModels,
isScanning: current.isScanning,
};
}
function blockWhileLifecycleBusy(
snapshot: RuntimeSnapshot,
command: RuntimeLifecycleCommand,
): RuntimeSnapshot | null {
if (snapshot.active_command === "idle") {
return null;
}
const blockedBy = snapshot.active_command;
const message =
blockedBy === "start"
? "Local API start already in progress."
: blockedBy === "stop"
? "Local API stop already in progress."
: blockedBy === "save"
? "Runtime settings save already in progress."
: "Runtime save and start already in progress.";
return {
...snapshot,
last_command_result: createRuntimeLifecycleResult(command, message, "blocked"),
};
}
function startLifecycleSnapshot(
snapshot: RuntimeSnapshot,
command: RuntimeLifecycleCommand,
status: RuntimeSnapshot["status"],
): RuntimeSnapshot {
return {
...snapshot,
status,
process_running: command === "start" ? true : snapshot.process_running,
ready: false,
last_error: null,
active_command: command,
};
}
function finalizeLifecycleSnapshot(
fallbackCommand: RuntimeLifecycleCommand,
fallbackMessage: string,
next: RuntimeSnapshot,
): RuntimeSnapshot {
const normalizedNext = normalizeSnapshot(next);
if (normalizedNext.last_command_result.command !== "idle") {
return normalizedNext;
}
return {
...normalizedNext,
last_command_result: createRuntimeLifecycleResult(fallbackCommand, fallbackMessage),
};
}
export function useRuntimeManager() {
const [draftState, setDraftState] = useState<RuntimeDraftState>(() =>
createRuntimeDraftState(createDefaultRuntimeSnapshot()),
);
const [loading, setLoading] = useState(true);
const [busy, setBusy] = useState(false);
const refresh = useCallback(async () => {
const next = await getRuntimeSnapshot();
setDraftState((current) => reconcileDraftState(current, next));
return next;
}, []);
useEffect(() => {
let active = true;
bootstrapRuntime()
.then((next) => {
if (active) {
setDraftState((current) => reconcileDraftState(current, next));
setLoading(false);
}
})
.catch(() => {
if (active) {
setLoading(false);
}
});
const id = window.setInterval(() => {
refresh().catch(() => undefined);
}, 3000);
return () => {
active = false;
window.clearInterval(id);
};
}, [refresh]);
const openDraft = useCallback(() => {
setDraftState((current) => {
const draftConfig = current.isDirty ? current.draftConfig : cloneConfig(current.savedBaseline);
// Run source-mode validation when opening draft
const validation = validateSourceMode(draftConfig);
// Initialize field validations based on validation results
const updatedFields = { ...current.fields };
for (const [fieldName, fieldValidation] of Object.entries(validation.fieldValidations)) {
updatedFields[fieldName as RuntimeDraftFieldKey] = {
invalid: !fieldValidation.valid,
pending: false,
message: fieldValidation.message,
};
}
return {
...current,
isOpen: true,
draftConfig,
fields: updatedFields,
};
});
}, []);
const closeDraft = useCallback(() => {
setDraftState((current) => ({
...current,
isOpen: false,
}));
}, []);
const resetDraft = useCallback(() => {
setDraftState((current) => {
const resetConfig = cloneConfig(current.savedBaseline);
// Run source-mode validation when resetting
const validation = validateSourceMode(resetConfig);
// Initialize field validations based on validation results
const updatedFields = createRuntimeDraftFieldState();
for (const [fieldName, fieldValidation] of Object.entries(validation.fieldValidations)) {
updatedFields[fieldName as RuntimeDraftFieldKey] = {
invalid: !fieldValidation.valid,
pending: false,
message: fieldValidation.message,
};
}
return {
...current,
draftConfig: resetConfig,
isDirty: false,
fields: updatedFields,
};
});
}, []);
const updateDraft = useCallback((patch: Partial<RuntimeConfig>) => {
setDraftState((current) => {
const nextDraft = {
...current.draftConfig,
...patch,
};
return {
...current,
isOpen: true,
isDirty: !areConfigsEqual(nextDraft, current.savedBaseline),
draftConfig: nextDraft,
};
});
}, []);
const updateDraftField = useCallback(<K extends RuntimeDraftFieldKey>(field: K, value: RuntimeConfig[K]) => {
setDraftState((current) => {
const nextDraft = {
...current.draftConfig,
[field]: value,
};
// Run source-mode validation
const validation = validateSourceMode(nextDraft);
// Update field validations based on validation results
const updatedFields = { ...current.fields };
for (const [fieldName, fieldValidation] of Object.entries(validation.fieldValidations)) {
updatedFields[fieldName as RuntimeDraftFieldKey] = {
invalid: !fieldValidation.valid,
pending: false,
message: fieldValidation.message,
};
}
return {
...current,
isOpen: true,
isDirty: !areConfigsEqual(nextDraft, current.savedBaseline),
draftConfig: nextDraft,
fields: updatedFields,
};
});
}, []);
const updateDraftFieldMeta = useCallback(
(field: RuntimeDraftFieldKey, patch: Partial<RuntimeDraftFieldMeta>) => {
setDraftState((current) => ({
...current,
fields: {
...current.fields,
[field]: {
...current.fields[field],
invalid: false,
pending: false,
message: null,
...patch,
},
},
}));
},
[],
);
// Phase 4: Model scanning for validation
const scanModels = useCallback(async (dirPath: string | null) => {
if (!dirPath) {
setDraftState((current) => ({
...current,
scannedModels: [],
isScanning: false,
}));
return;
}
// Set scanning state first
setDraftState((current) => ({
...current,
isScanning: true,
}));
try {
const models = await scanRuntimeModels(dirPath);
// Now update with results
setDraftState((current) => {
const modelPathInvalid = current.draftConfig.model_path
? !models.some((m) => m.path === current.draftConfig.model_path)
: false;
const modelPathMessage = current.draftConfig.model_path
? models.some((m) => m.path === current.draftConfig.model_path)
? null
: "Model file not found in current models directory"
: null;
return {
...current,
scannedModels: models,
isScanning: false,
// Validate model_path against scanned models
fields: {
...current.fields,
model_path: {
invalid: modelPathInvalid,
pending: false,
message: modelPathMessage,
},
},
};
});
} catch (error) {
setDraftState((current) => ({
...current,
scannedModels: [],
isScanning: false,
fields: {
...current.fields,
models_dir: {
invalid: true,
pending: false,
message: error instanceof Error ? error.message : String(error),
},
},
}));
}
}, []);
const commitSavedSnapshot = useCallback((next: RuntimeSnapshot, keepOpen: boolean) => {
setDraftState((current) => ({
...current,
liveSnapshot: next,
savedBaseline: cloneConfig(next.config),
draftConfig: cloneConfig(next.config),
isDirty: false,
isOpen: keepOpen,
fields: createRuntimeDraftFieldState(),
}));
}, []);
const saveDraft = useCallback(async () => {
const blockedSnapshot = blockWhileLifecycleBusy(draftState.liveSnapshot, "save");
if (blockedSnapshot) {
setDraftState((current) => ({ ...current, liveSnapshot: blockedSnapshot }));
return blockedSnapshot;
}
setBusy(true);
setDraftState((current) => ({
...current,
liveSnapshot: startLifecycleSnapshot(current.liveSnapshot, "save", current.liveSnapshot.status),
}));
try {
const next = await saveRuntimeConfig(draftState.draftConfig);
const resolvedNext = finalizeLifecycleSnapshot("save", "Save completed.", next);
commitSavedSnapshot(resolvedNext, true);
return resolvedNext;
} finally {
setBusy(false);
}
}, [commitSavedSnapshot, draftState.draftConfig]);
const saveConfig = useCallback(async (patch: Partial<RuntimeConfig>) => {
const blockedSnapshot = blockWhileLifecycleBusy(draftState.liveSnapshot, "save");
if (blockedSnapshot) {
setDraftState((current) => ({ ...current, liveSnapshot: blockedSnapshot }));
return blockedSnapshot;
}
setBusy(true);
setDraftState((current) => ({
...current,
liveSnapshot: startLifecycleSnapshot(current.liveSnapshot, "save", current.liveSnapshot.status),
}));
try {
const next = await saveRuntimeConfig({ ...draftState.draftConfig, ...patch });
const resolvedNext = finalizeLifecycleSnapshot("save", "Save completed.", next);
commitSavedSnapshot(resolvedNext, draftState.isOpen);
return resolvedNext;
} finally {
setBusy(false);
}
}, [commitSavedSnapshot, draftState.draftConfig, draftState.isOpen]);
const startLocal = useCallback(async () => {
const blockedSnapshot = blockWhileLifecycleBusy(draftState.liveSnapshot, "start");
if (blockedSnapshot) {
setDraftState((current) => ({ ...current, liveSnapshot: blockedSnapshot }));
return blockedSnapshot;
}
setBusy(true);
setDraftState((current) => ({
...current,
liveSnapshot: startLifecycleSnapshot(current.liveSnapshot, "start", "starting"),
}));
try {
const next = await startRuntime();
const resolvedNext = finalizeLifecycleSnapshot("start", "Local API started.", next);
setDraftState((current) => reconcileDraftState(current, resolvedNext));
return resolvedNext;
} finally {
setBusy(false);
}
}, [draftState.liveSnapshot]);
const stopLocal = useCallback(async () => {
const blockedSnapshot = blockWhileLifecycleBusy(draftState.liveSnapshot, "stop");
if (blockedSnapshot) {
setDraftState((current) => ({ ...current, liveSnapshot: blockedSnapshot }));
return blockedSnapshot;
}
setBusy(true);
setDraftState((current) => ({
...current,
liveSnapshot: startLifecycleSnapshot(current.liveSnapshot, "stop", "stopping"),
}));
try {
const next = await stopRuntime();
const resolvedNext = finalizeLifecycleSnapshot("stop", "Local API stopped.", next);
setDraftState((current) => reconcileDraftState(current, resolvedNext));
return resolvedNext;
} finally {
setBusy(false);
}
}, [draftState.liveSnapshot]);
const connection = useMemo(() => getRuntimeConnection(draftState.liveSnapshot), [draftState.liveSnapshot]);
const activeLifecycleCommand = draftState.liveSnapshot.active_command;
const lastLifecycleResult: RuntimeLifecycleResult = draftState.liveSnapshot.last_command_result;
const lifecycleBusy = activeLifecycleCommand !== "idle";
const applyImpact: RuntimeApplyImpact = useMemo(
() =>
getRuntimeApplyImpact({
savedBaseline: draftState.savedBaseline,
draftConfig: draftState.draftConfig,
processRunning: draftState.liveSnapshot.process_running,
}),
[draftState.draftConfig, draftState.liveSnapshot.process_running, draftState.savedBaseline],
);
// Phase 3: Unified workspace contract
const workspaceSummary: RuntimeWorkspaceSummary = useMemo(
() => getRuntimeWorkspaceSummary(draftState.liveSnapshot),
[draftState.liveSnapshot],
);
const workspaceHealth: RuntimeWorkspaceHealth = useMemo(
() => getRuntimeHealthSummary(draftState.liveSnapshot),
[draftState.liveSnapshot],
);
return {
snapshot: draftState.liveSnapshot,
liveSnapshot: draftState.liveSnapshot,
savedBaseline: draftState.savedBaseline,
draftConfig: draftState.draftConfig,
draftDirty: draftState.isDirty,
draftOpen: draftState.isOpen,
draftFields: draftState.fields,
connection,
loading,
busy,
activeLifecycleCommand,
lastLifecycleResult,
lifecycleBusy,
applyImpact,
// Phase 3: Unified workspace contract
workspaceSummary,
workspaceHealth,
refresh,
openDraft,
closeDraft,
resetDraft,
updateDraft,
updateDraftField,
updateDraftFieldMeta,
saveDraft,
saveConfig,
startLocal,
stopLocal,
// Phase 4: Model scanning for validation
scannedModels: draftState.scannedModels,
isScanningModels: draftState.isScanning,
scanModels,
};
}