using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json.Linq; using SwarmUI.Accounts; using SwarmUI.Core; using SwarmUI.Text2Image; using SwarmUI.Utils; namespace Mrleo1nid.SwarmAssistent; /// Server-side model inventory (LoRA / checkpoint / wildcard lists). public partial class SwarmAssistentExtension { const int MaxLorasInInventoryFallback = 150; const int MaxWildcardsInInventoryFallback = 80; const int MaxCheckpointsInInventoryFallback = 60; const int InventoryBlurbMaxFallback = 140; /// Server-side LoRA / checkpoint / wildcard inventory (not DOM scrape). /// Pass rescan=true after downloads so new files appear (calls Program.RefreshAllModelSets). public async Task AssistentListInventory(Session session, bool rescan = false) { await Task.CompletedTask; if (rescan) { try { Program.RefreshAllModelSets(); } catch (Exception ex) { Logs.Debug($"AssistentListInventory rescan: {ex.Message}"); try { Program.ModelRefreshEvent?.Invoke(); } catch (Exception ex2) { Logs.Debug($"AssistentListInventory ModelRefreshEvent: {ex2.Message}"); } } } JArray loras = []; JArray checkpoints = []; JArray wildcards = []; if (Program.T2IModelSets.TryGetValue("LoRA", out T2IModelHandler loraHandler)) { foreach (T2IModel model in loraHandler.Models.Values .OrderByDescending(m => LooksLikeKreaArch(m)) .ThenBy(m => m.Name) .Take(CfgInt("max_loras_inventory", MaxLorasInInventoryFallback))) { loras.Add(BuildInventoryModelEntry(model, "lora")); } } if (Program.T2IModelSets.TryGetValue("Stable-Diffusion", out T2IModelHandler ckptHandler)) { foreach (T2IModel model in ckptHandler.Models.Values .OrderByDescending(m => LooksLikeKreaArch(m)) .ThenBy(m => m.Name) .Take(CfgInt("max_checkpoints_inventory", MaxCheckpointsInInventoryFallback))) { checkpoints.Add(BuildInventoryModelEntry(model, "checkpoint")); } } try { foreach (string name in WildcardsHelper.ListFiles.OrderBy(n => n).Take(CfgInt("max_wildcards_inventory", MaxWildcardsInInventoryFallback))) { wildcards.Add(new JObject { ["name"] = name }); } } catch (Exception ex) { Logs.Debug($"AssistentListInventory wildcards: {ex.Message}"); } bool hasCivitaiKey = !string.IsNullOrWhiteSpace(session.User.GetGenericData("civitai_api", "key")); return new JObject { ["success"] = true, ["loras"] = loras, ["checkpoints"] = checkpoints, ["wildcards"] = wildcards, ["has_civitai_key"] = hasCivitaiKey, ["rescanned"] = rescan, ["inventory_at"] = DateTimeOffset.UtcNow.ToUnixTimeSeconds(), }; } static bool LooksLikeKreaArch(T2IModel model) { string arch = model?.ModelClass?.ID ?? ""; string compat = model?.ModelClass?.CompatClass?.ID ?? ""; string name = model?.Name ?? ""; string blob = $"{arch} {compat} {name}".ToLowerInvariant(); return blob.Contains("krea"); } JObject BuildInventoryModelEntry(T2IModel model, string kind) { string weight = null; try { weight = model.RawFilePath; } catch { /* ignore */ } string usage = model.Metadata?.UsageHint; string desc = model.Metadata?.Description; try { if (string.IsNullOrWhiteSpace(desc) && !string.IsNullOrWhiteSpace(model.Description)) { desc = model.Description; } } catch { // older Swarm builds } string blurb = null; string raw = !string.IsNullOrWhiteSpace(usage) ? usage : desc; if (!string.IsNullOrWhiteSpace(raw)) { blurb = Clip(CollapseWs(raw), CfgInt("inventory_blurb_max", InventoryBlurbMaxFallback)); } JArray tags = null; if (model.Metadata?.Tags is { Length: > 0 } tagArr) { tags = new JArray(tagArr.Where(t => !string.IsNullOrWhiteSpace(t)).Take(8)); } string trigger = model.Metadata?.TriggerPhrase; JArray triggers = null; if (!string.IsNullOrWhiteSpace(trigger)) { triggers = new JArray(trigger.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Take(12)); } JObject entry = new() { ["name"] = model.Name, ["title"] = model.Metadata?.Title ?? model.Title ?? model.Name, ["kind"] = kind, ["trigger_phrase"] = trigger, ["architecture"] = model.ModelClass?.ID, ["compat_class"] = model.ModelClass?.CompatClass?.ID, ["hash"] = model.Metadata?.Hash ?? "", ["krea_likely"] = LooksLikeKreaArch(model), }; if (!string.IsNullOrWhiteSpace(weight)) { string stem = Path.GetFileNameWithoutExtension(weight); string dir = Path.GetDirectoryName(weight); string side = Path.Combine(dir ?? "", $"{stem}.civitai.json"); entry["has_sidecar"] = File.Exists(side); foreach (string suffix in new[] { ".preview.jpg", ".preview.png", ".preview.jpeg", ".jpg", ".png", ".webp" }) { string prev = Path.Combine(dir ?? "", stem + suffix); if (File.Exists(prev)) { string folder = kind == "lora" ? "Lora" : "Stable-Diffusion"; entry["preview_url"] = $"View/Models/{folder}/{Path.GetFileName(prev)}"; break; } } } else { entry["has_sidecar"] = false; } if (triggers is not null && triggers.Count > 0) { entry["triggers"] = triggers; } if (!string.IsNullOrWhiteSpace(blurb)) { entry["blurb"] = blurb; } if (!string.IsNullOrWhiteSpace(usage)) { entry["usage_hint"] = Clip(CollapseWs(usage), 120); } if (tags is not null && tags.Count > 0) { entry["tags"] = tags; } string defW = model.Metadata?.LoraDefaultWeight; if (!string.IsNullOrWhiteSpace(defW) && kind == "lora") { entry["default_weight"] = defW; } return entry; } }