omnius 1.0.198 → 1.0.200
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +974 -31
- package/docs/rest/QUICKREF.md +2 -2
- package/docs/rest/auth-and-scopes.md +1 -1
- package/docs/rest/endpoints/run.md +2 -2
- package/docs/rest/endpoints/tools.md +5 -5
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23044,6 +23044,13 @@ function deleteRepoFromCache(repo) {
|
|
|
23044
23044
|
}
|
|
23045
23045
|
return removed;
|
|
23046
23046
|
}
|
|
23047
|
+
function deleteCachedModel(repo) {
|
|
23048
|
+
const bytesFreed = deleteRepoFromCache(repo);
|
|
23049
|
+
const meta = readMeta();
|
|
23050
|
+
meta.entries = meta.entries.filter((entry) => entry.repo !== repo);
|
|
23051
|
+
writeMeta(meta);
|
|
23052
|
+
return { repo, bytesFreed };
|
|
23053
|
+
}
|
|
23047
23054
|
function evictModelsToFreeSpace(args) {
|
|
23048
23055
|
const safetyMargin = args.safetyMarginBytes ?? 1 * 1024 ** 3;
|
|
23049
23056
|
const target = args.neededBytes + safetyMargin;
|
|
@@ -260097,6 +260104,31 @@ function approxImageDownloadBytes(preset) {
|
|
|
260097
260104
|
}
|
|
260098
260105
|
return gigabytesToBytes(4);
|
|
260099
260106
|
}
|
|
260107
|
+
function imagePresetDependencyModels(preset, selectedModel) {
|
|
260108
|
+
const models = [
|
|
260109
|
+
selectedModel,
|
|
260110
|
+
preset?.diffusersBaseModel,
|
|
260111
|
+
preset?.textEncoderModel,
|
|
260112
|
+
...preset?.loraAdapters?.map((adapter) => adapter.repoId) ?? []
|
|
260113
|
+
].filter((value2) => Boolean(value2 && value2.trim()));
|
|
260114
|
+
return [...new Set(models)];
|
|
260115
|
+
}
|
|
260116
|
+
function diffusersRunnerModelArgs(model) {
|
|
260117
|
+
const preset = getImageGenerationPreset(model);
|
|
260118
|
+
const runnerModel = preset?.diffusersBaseModel ?? model;
|
|
260119
|
+
const argv = ["--model", runnerModel];
|
|
260120
|
+
if (runnerModel !== model)
|
|
260121
|
+
argv.push("--display-model", model);
|
|
260122
|
+
if (preset?.textEncoderModel) {
|
|
260123
|
+
argv.push("--text-encoder", preset.textEncoderModel);
|
|
260124
|
+
if (preset.textEncoderTarget)
|
|
260125
|
+
argv.push("--text-encoder-target", preset.textEncoderTarget);
|
|
260126
|
+
}
|
|
260127
|
+
for (const adapter of preset?.loraAdapters ?? []) {
|
|
260128
|
+
argv.push("--lora", JSON.stringify(adapter));
|
|
260129
|
+
}
|
|
260130
|
+
return argv;
|
|
260131
|
+
}
|
|
260100
260132
|
async function ensureImageGenerationCacheDirs(repoRoot) {
|
|
260101
260133
|
const env2 = imageGenerationPythonEnv(repoRoot);
|
|
260102
260134
|
await Promise.all([
|
|
@@ -260228,7 +260260,7 @@ function parseRunnerJson(stdout) {
|
|
|
260228
260260
|
}
|
|
260229
260261
|
return null;
|
|
260230
260262
|
}
|
|
260231
|
-
var DEFAULT_DIFFUSERS_IMAGE_MODEL, DEFAULT_OLLAMA_IMAGE_MODEL, LEGACY_SDXL_TURBO_MODEL, SANA_1_5_1_6B_MODEL, SANA_1_5_4_8B_MODEL, SANA_1_6B_MULTILING_MODEL, SANA_1_6B_2K_MODEL, SANA_1_6B_4K_MODEL, SANA_SPRINT_0_6B_MODEL, SECONDARY_FLUX_DEV_MODEL, SECONDARY_FLUX_DEV_MIRROR_MODEL, SECONDARY_FLUX_DEV_COMFY_MODEL, SECONDARY_FLUX_FILL_MODEL, SECONDARY_FLUX_FILL_FP8_MODEL, SECONDARY_FLUX2_MODEL, OFFICIAL_BFL_ORG, IMAGE_GENERATION_MODEL_REPLACEMENTS, DIFFUSERS_PYTHON_PACKAGES, SDCPP_PYTHON_PACKAGES, IMAGE_GENERATION_MODEL_PRESETS, IMAGE_GENERATION_QUALITY_LADDER, OLLAMA_IMAGE_MODELS, DIFFUSERS_RUNNER, SDCPP_RUNNER, ImageGenerateTool;
|
|
260263
|
+
var DEFAULT_DIFFUSERS_IMAGE_MODEL, DEFAULT_OLLAMA_IMAGE_MODEL, LEGACY_SDXL_TURBO_MODEL, SANA_1_5_1_6B_MODEL, SANA_1_5_4_8B_MODEL, SANA_1_6B_MULTILING_MODEL, SANA_1_6B_2K_MODEL, SANA_1_6B_4K_MODEL, SANA_SPRINT_0_6B_MODEL, SECONDARY_FLUX_DEV_MODEL, SECONDARY_FLUX_DEV_MIRROR_MODEL, SECONDARY_FLUX_DEV_COMFY_MODEL, SECONDARY_FLUX_FILL_MODEL, SECONDARY_FLUX_FILL_FP8_MODEL, SECONDARY_FLUX2_MODEL, OFFICIAL_FLUX1_DEV_MODEL, OFFICIAL_FLUX2_KLEIN_9B_MODEL, PONPOKE_FLUX2_UNCENSORED_TEXT_ENCODER_MODEL, LUSTLY_FLUX_UNCENSORED_LORA_MODEL, KENERATE_FLUX_UNCENSORED_LORA_MODEL, OFFICIAL_BFL_ORG, IMAGE_GENERATION_MODEL_REPLACEMENTS, DIFFUSERS_PYTHON_PACKAGES, SDCPP_PYTHON_PACKAGES, IMAGE_GENERATION_MODEL_PRESETS, IMAGE_GENERATION_QUALITY_LADDER, OLLAMA_IMAGE_MODELS, DIFFUSERS_RUNNER, SDCPP_RUNNER, ImageGenerateTool;
|
|
260232
260264
|
var init_image_generate = __esm({
|
|
260233
260265
|
"packages/execution/dist/tools/image-generate.js"() {
|
|
260234
260266
|
"use strict";
|
|
@@ -260252,6 +260284,11 @@ var init_image_generate = __esm({
|
|
|
260252
260284
|
SECONDARY_FLUX_FILL_MODEL = "diffusers/FLUX.1-Fill-dev-nf4";
|
|
260253
260285
|
SECONDARY_FLUX_FILL_FP8_MODEL = "boricuapab/flux1-fill-dev-fp8";
|
|
260254
260286
|
SECONDARY_FLUX2_MODEL = "x/flux2-klein";
|
|
260287
|
+
OFFICIAL_FLUX1_DEV_MODEL = "black-forest-labs/FLUX.1-dev";
|
|
260288
|
+
OFFICIAL_FLUX2_KLEIN_9B_MODEL = "black-forest-labs/FLUX.2-klein-9B";
|
|
260289
|
+
PONPOKE_FLUX2_UNCENSORED_TEXT_ENCODER_MODEL = "ponpoke/flux2-klein-9b-uncensored-text-encoder";
|
|
260290
|
+
LUSTLY_FLUX_UNCENSORED_LORA_MODEL = "lustlyai/Flux_Lustly.ai_Uncensored_nsfw_v1";
|
|
260291
|
+
KENERATE_FLUX_UNCENSORED_LORA_MODEL = "kenerateai/Flux-uncensored";
|
|
260255
260292
|
OFFICIAL_BFL_ORG = "black-forest-labs";
|
|
260256
260293
|
IMAGE_GENERATION_MODEL_REPLACEMENTS = /* @__PURE__ */ new Map([
|
|
260257
260294
|
[officialBflModel("FLUX.1-dev"), SECONDARY_FLUX_DEV_MODEL],
|
|
@@ -260274,6 +260311,7 @@ var init_image_generate = __esm({
|
|
|
260274
260311
|
"transformers",
|
|
260275
260312
|
"accelerate",
|
|
260276
260313
|
"safetensors",
|
|
260314
|
+
"peft",
|
|
260277
260315
|
"pillow",
|
|
260278
260316
|
"sentencepiece",
|
|
260279
260317
|
"protobuf"
|
|
@@ -260387,6 +260425,76 @@ var init_image_generate = __esm({
|
|
|
260387
260425
|
fallbackFor: [SECONDARY_FLUX_FILL_MODEL],
|
|
260388
260426
|
note: "Traceable FP8 fallback for FLUX.1 Fill dev from the research package."
|
|
260389
260427
|
},
|
|
260428
|
+
{
|
|
260429
|
+
id: PONPOKE_FLUX2_UNCENSORED_TEXT_ENCODER_MODEL,
|
|
260430
|
+
label: "FLUX.2 Klein 9B uncensored text encoder",
|
|
260431
|
+
backend: "diffusers",
|
|
260432
|
+
install: 'python3 .omnius/image-gen/diffusers_text2image.py --model black-forest-labs/FLUX.2-klein-9B --text-encoder ponpoke/flux2-klein-9b-uncensored-text-encoder --steps 4 --guidance 1 --width 1024 --height 1024 --prompt "..." --output .omnius/images/out.png',
|
|
260433
|
+
category: "Adult-capable FLUX adapter",
|
|
260434
|
+
sizeClass: "FLUX.2 Klein 9B text-encoder override",
|
|
260435
|
+
quality: "Uncensored FLUX.2 Klein text-encoder override; uses the official FLUX.2 Klein 9B base pipeline and replaces the prompt encoder when the installed Diffusers stack supports that component layout.",
|
|
260436
|
+
minVramGB: 24,
|
|
260437
|
+
recommendedVramGB: 32,
|
|
260438
|
+
deployment: "Diffusers Flux2KleinPipeline over black-forest-labs/FLUX.2-klein-9B with a replacement text encoder. Requires Hugging Face access/license acceptance for the BFL base and the text-encoder repo.",
|
|
260439
|
+
steps: 4,
|
|
260440
|
+
guidance: 1,
|
|
260441
|
+
width: 1024,
|
|
260442
|
+
height: 1024,
|
|
260443
|
+
diffusersBaseModel: OFFICIAL_FLUX2_KLEIN_9B_MODEL,
|
|
260444
|
+
textEncoderModel: PONPOKE_FLUX2_UNCENSORED_TEXT_ENCODER_MODEL,
|
|
260445
|
+
textEncoderTarget: "auto",
|
|
260446
|
+
approxDownloadGB: 36,
|
|
260447
|
+
note: "Adapter-style preset: loads the gated FLUX.2 Klein 9B base and swaps in the ponpoke text encoder. Not in the automatic fallback ladder."
|
|
260448
|
+
},
|
|
260449
|
+
{
|
|
260450
|
+
id: LUSTLY_FLUX_UNCENSORED_LORA_MODEL,
|
|
260451
|
+
label: "Flux Lustly uncensored LoRA",
|
|
260452
|
+
backend: "diffusers",
|
|
260453
|
+
install: `python3 .omnius/image-gen/diffusers_text2image.py --model black-forest-labs/FLUX.1-dev --lora '{"repoId":"lustlyai/Flux_Lustly.ai_Uncensored_nsfw_v1","weightName":"flux_lustly-ai_v1.safetensors","adapterName":"v1","adapterWeight":1}' --steps 20 --guidance 4 --width 768 --height 768 --prompt "..." --output .omnius/images/out.png`,
|
|
260454
|
+
category: "Adult-capable FLUX adapter",
|
|
260455
|
+
sizeClass: "FLUX.1-dev LoRA adapter",
|
|
260456
|
+
quality: "Adult-capable FLUX.1-dev LoRA tested by its publisher on full FLUX dev and schnell. Listed as an explicit opt-in model, not part of automatic fallback.",
|
|
260457
|
+
minVramGB: 16,
|
|
260458
|
+
recommendedVramGB: 24,
|
|
260459
|
+
deployment: "Diffusers LoRA over black-forest-labs/FLUX.1-dev. Requires Hugging Face access/license acceptance for the gated BFL base model and the adapter terms.",
|
|
260460
|
+
steps: 20,
|
|
260461
|
+
guidance: 4,
|
|
260462
|
+
width: 768,
|
|
260463
|
+
height: 768,
|
|
260464
|
+
diffusersBaseModel: OFFICIAL_FLUX1_DEV_MODEL,
|
|
260465
|
+
loraAdapters: [{
|
|
260466
|
+
repoId: LUSTLY_FLUX_UNCENSORED_LORA_MODEL,
|
|
260467
|
+
weightName: "flux_lustly-ai_v1.safetensors",
|
|
260468
|
+
adapterName: "v1",
|
|
260469
|
+
adapterWeight: 1
|
|
260470
|
+
}],
|
|
260471
|
+
approxDownloadGB: 24,
|
|
260472
|
+
note: "Adapter-style preset: loads FLUX.1-dev then applies the Lustly LoRA. Explicit selection only."
|
|
260473
|
+
},
|
|
260474
|
+
{
|
|
260475
|
+
id: KENERATE_FLUX_UNCENSORED_LORA_MODEL,
|
|
260476
|
+
label: "Kenerate Flux uncensored LoRA",
|
|
260477
|
+
backend: "diffusers",
|
|
260478
|
+
install: `python3 .omnius/image-gen/diffusers_text2image.py --model black-forest-labs/FLUX.1-dev --lora '{"repoId":"kenerateai/Flux-uncensored","adapterName":"kenerate","adapterWeight":1}' --steps 20 --guidance 3.5 --width 1024 --height 1024 --prompt "..." --output .omnius/images/out.png`,
|
|
260479
|
+
category: "Adult-capable FLUX adapter",
|
|
260480
|
+
sizeClass: "FLUX.1-dev LoRA adapter",
|
|
260481
|
+
quality: "Community FLUX.1-dev LoRA. Hugging Face currently marks the model card as removed, so availability may fail at download time; kept as a selectable explicit model because the repo still advertises a Diffusers LoRA load path.",
|
|
260482
|
+
minVramGB: 16,
|
|
260483
|
+
recommendedVramGB: 24,
|
|
260484
|
+
deployment: "Diffusers LoRA over black-forest-labs/FLUX.1-dev. Requires Hugging Face access/license acceptance for the gated BFL base model; adapter availability may vary.",
|
|
260485
|
+
steps: 20,
|
|
260486
|
+
guidance: 3.5,
|
|
260487
|
+
width: 1024,
|
|
260488
|
+
height: 1024,
|
|
260489
|
+
diffusersBaseModel: OFFICIAL_FLUX1_DEV_MODEL,
|
|
260490
|
+
loraAdapters: [{
|
|
260491
|
+
repoId: KENERATE_FLUX_UNCENSORED_LORA_MODEL,
|
|
260492
|
+
adapterName: "kenerate",
|
|
260493
|
+
adapterWeight: 1
|
|
260494
|
+
}],
|
|
260495
|
+
approxDownloadGB: 24,
|
|
260496
|
+
note: "Adapter-style preset: loads FLUX.1-dev then applies the Kenerate LoRA. The upstream card says the LoRA has been removed, so failures should be surfaced directly."
|
|
260497
|
+
},
|
|
260390
260498
|
{
|
|
260391
260499
|
id: "stabilityai/stable-diffusion-3.5-large",
|
|
260392
260500
|
label: "Stable Diffusion 3.5 Large",
|
|
@@ -260723,6 +260831,13 @@ def _device():
|
|
|
260723
260831
|
|
|
260724
260832
|
def _pipeline_class(model):
|
|
260725
260833
|
lowered = model.lower()
|
|
260834
|
+
if "flux.2" in lowered or "flux2" in lowered or "flux-2" in lowered:
|
|
260835
|
+
try:
|
|
260836
|
+
from diffusers import Flux2KleinPipeline
|
|
260837
|
+
return Flux2KleinPipeline
|
|
260838
|
+
except Exception:
|
|
260839
|
+
from diffusers import DiffusionPipeline
|
|
260840
|
+
return DiffusionPipeline
|
|
260726
260841
|
if "flux" in lowered:
|
|
260727
260842
|
from diffusers import FluxPipeline
|
|
260728
260843
|
return FluxPipeline
|
|
@@ -260749,13 +260864,85 @@ def _pipeline_class(model):
|
|
|
260749
260864
|
def _large_model(model):
|
|
260750
260865
|
lowered = model.lower()
|
|
260751
260866
|
return any(token in lowered for token in [
|
|
260752
|
-
"flux.1", "flux.2", "stable-diffusion-3.5", "hunyuan", "janus",
|
|
260867
|
+
"flux.1", "flux.2", "flux2", "stable-diffusion-3.5", "hunyuan", "janus",
|
|
260753
260868
|
"sana1.5_4.8b", "sana_1600m_2kpx", "sana_1600m_4kpx",
|
|
260754
260869
|
])
|
|
260755
260870
|
|
|
260871
|
+
def _parse_lora_specs(raw_specs):
|
|
260872
|
+
specs = []
|
|
260873
|
+
for raw in raw_specs or []:
|
|
260874
|
+
if not raw:
|
|
260875
|
+
continue
|
|
260876
|
+
try:
|
|
260877
|
+
parsed = json.loads(raw)
|
|
260878
|
+
if isinstance(parsed, str):
|
|
260879
|
+
parsed = {"repoId": parsed}
|
|
260880
|
+
if isinstance(parsed, dict) and parsed.get("repoId"):
|
|
260881
|
+
specs.append(parsed)
|
|
260882
|
+
except Exception as exc:
|
|
260883
|
+
raise ValueError(f"Invalid --lora JSON {raw!r}: {exc}")
|
|
260884
|
+
return specs
|
|
260885
|
+
|
|
260886
|
+
def _load_lora_adapters(pipe, specs):
|
|
260887
|
+
adapter_names = []
|
|
260888
|
+
adapter_weights = []
|
|
260889
|
+
for index, spec in enumerate(specs):
|
|
260890
|
+
repo_id = str(spec.get("repoId") or "").strip()
|
|
260891
|
+
if not repo_id:
|
|
260892
|
+
continue
|
|
260893
|
+
kwargs = {}
|
|
260894
|
+
weight_name = str(spec.get("weightName") or "").strip()
|
|
260895
|
+
adapter_name = str(spec.get("adapterName") or f"adapter_{index}").strip()
|
|
260896
|
+
if weight_name:
|
|
260897
|
+
kwargs["weight_name"] = weight_name
|
|
260898
|
+
if adapter_name:
|
|
260899
|
+
kwargs["adapter_name"] = adapter_name
|
|
260900
|
+
_progress("load", f"loading LoRA adapter {repo_id}")
|
|
260901
|
+
pipe.load_lora_weights(repo_id, **kwargs)
|
|
260902
|
+
if adapter_name:
|
|
260903
|
+
adapter_names.append(adapter_name)
|
|
260904
|
+
adapter_weights.append(float(spec.get("adapterWeight", 1.0)))
|
|
260905
|
+
if adapter_names and hasattr(pipe, "set_adapters"):
|
|
260906
|
+
_progress("load", f"activating {len(adapter_names)} LoRA adapter(s)")
|
|
260907
|
+
pipe.set_adapters(adapter_names, adapter_weights=adapter_weights)
|
|
260908
|
+
|
|
260909
|
+
def _target_text_encoder_attrs(pipe, target, repo_id):
|
|
260910
|
+
explicit = str(target or "").strip()
|
|
260911
|
+
if explicit and explicit != "auto":
|
|
260912
|
+
return [explicit]
|
|
260913
|
+
lowered = repo_id.lower()
|
|
260914
|
+
preferred = ["text_encoder_3", "text_encoder_2", "text_encoder"] if ("qwen" in lowered or "flux2" in lowered or "klein" in lowered) else ["text_encoder", "text_encoder_2", "text_encoder_3"]
|
|
260915
|
+
return [name for name in preferred if hasattr(pipe, name)]
|
|
260916
|
+
|
|
260917
|
+
def _tokenizer_attr_for_text_encoder(attr):
|
|
260918
|
+
if attr == "text_encoder":
|
|
260919
|
+
return "tokenizer"
|
|
260920
|
+
if attr.startswith("text_encoder_"):
|
|
260921
|
+
return "tokenizer_" + attr.split("_")[-1]
|
|
260922
|
+
return "tokenizer"
|
|
260923
|
+
|
|
260924
|
+
def _replace_text_encoder(pipe, repo_id, target, dtype):
|
|
260925
|
+
if not repo_id:
|
|
260926
|
+
return
|
|
260927
|
+
from transformers import AutoModel, AutoTokenizer
|
|
260928
|
+
attrs = _target_text_encoder_attrs(pipe, target, repo_id)
|
|
260929
|
+
if not attrs:
|
|
260930
|
+
raise ValueError(f"Pipeline has no text_encoder component compatible with text encoder override {repo_id}")
|
|
260931
|
+
attr = attrs[0]
|
|
260932
|
+
tokenizer_attr = _tokenizer_attr_for_text_encoder(attr)
|
|
260933
|
+
_progress("load", f"loading replacement text encoder {repo_id} into {attr}")
|
|
260934
|
+
tokenizer = AutoTokenizer.from_pretrained(repo_id, trust_remote_code=True)
|
|
260935
|
+
text_encoder = AutoModel.from_pretrained(repo_id, torch_dtype=dtype, trust_remote_code=True)
|
|
260936
|
+
setattr(pipe, attr, text_encoder)
|
|
260937
|
+
if hasattr(pipe, tokenizer_attr):
|
|
260938
|
+
setattr(pipe, tokenizer_attr, tokenizer)
|
|
260939
|
+
else:
|
|
260940
|
+
_progress("load", f"pipeline has no {tokenizer_attr}; replaced {attr} only")
|
|
260941
|
+
|
|
260756
260942
|
def main():
|
|
260757
260943
|
parser = argparse.ArgumentParser()
|
|
260758
260944
|
parser.add_argument("--model", required=True)
|
|
260945
|
+
parser.add_argument("--display-model", default="")
|
|
260759
260946
|
parser.add_argument("--prompt", required=True)
|
|
260760
260947
|
parser.add_argument("--output", required=True)
|
|
260761
260948
|
parser.add_argument("--width", type=int, default=512)
|
|
@@ -260765,6 +260952,9 @@ def main():
|
|
|
260765
260952
|
parser.add_argument("--seed", type=int, default=None)
|
|
260766
260953
|
parser.add_argument("--device", default="auto")
|
|
260767
260954
|
parser.add_argument("--variant", default="")
|
|
260955
|
+
parser.add_argument("--lora", action="append", default=[])
|
|
260956
|
+
parser.add_argument("--text-encoder", default="")
|
|
260957
|
+
parser.add_argument("--text-encoder-target", default="auto")
|
|
260768
260958
|
parser.add_argument("--prewarm", action="store_true")
|
|
260769
260959
|
args = parser.parse_args()
|
|
260770
260960
|
|
|
@@ -260793,12 +260983,19 @@ def main():
|
|
|
260793
260983
|
pipe = pipeline_cls.from_pretrained(args.model, **kwargs)
|
|
260794
260984
|
_progress("load", f"model loaded on {device}")
|
|
260795
260985
|
|
|
260986
|
+
if args.text_encoder:
|
|
260987
|
+
_replace_text_encoder(pipe, args.text_encoder, args.text_encoder_target, dtype)
|
|
260988
|
+
|
|
260796
260989
|
if "sana" in lowered_model and hasattr(pipe, "text_encoder") and pipe.text_encoder is not None:
|
|
260797
260990
|
try:
|
|
260798
260991
|
pipe.text_encoder.to(torch.bfloat16)
|
|
260799
260992
|
except Exception:
|
|
260800
260993
|
pass
|
|
260801
260994
|
|
|
260995
|
+
lora_specs = _parse_lora_specs(args.lora)
|
|
260996
|
+
if lora_specs:
|
|
260997
|
+
_load_lora_adapters(pipe, lora_specs)
|
|
260998
|
+
|
|
260802
260999
|
if hasattr(pipe, "enable_attention_slicing"):
|
|
260803
261000
|
try:
|
|
260804
261001
|
pipe.enable_attention_slicing()
|
|
@@ -260817,7 +261014,10 @@ def main():
|
|
|
260817
261014
|
print(json.dumps({
|
|
260818
261015
|
"ok": True,
|
|
260819
261016
|
"path": "",
|
|
260820
|
-
"model": args.model,
|
|
261017
|
+
"model": args.display_model or args.model,
|
|
261018
|
+
"base_model": args.model,
|
|
261019
|
+
"lora_adapters": [spec.get("repoId") for spec in lora_specs],
|
|
261020
|
+
"text_encoder": args.text_encoder or None,
|
|
260821
261021
|
"backend": "diffusers",
|
|
260822
261022
|
"device": device,
|
|
260823
261023
|
"prewarm": True,
|
|
@@ -260853,7 +261053,10 @@ def main():
|
|
|
260853
261053
|
print(json.dumps({
|
|
260854
261054
|
"ok": True,
|
|
260855
261055
|
"path": str(out),
|
|
260856
|
-
"model": args.model,
|
|
261056
|
+
"model": args.display_model or args.model,
|
|
261057
|
+
"base_model": args.model,
|
|
261058
|
+
"lora_adapters": [spec.get("repoId") for spec in lora_specs],
|
|
261059
|
+
"text_encoder": args.text_encoder or None,
|
|
260857
261060
|
"backend": "diffusers",
|
|
260858
261061
|
"device": device,
|
|
260859
261062
|
"seconds": round(time.perf_counter() - t0, 3),
|
|
@@ -260913,7 +261116,7 @@ if __name__ == "__main__":
|
|
|
260913
261116
|
`;
|
|
260914
261117
|
ImageGenerateTool = class {
|
|
260915
261118
|
name = "generate_image";
|
|
260916
|
-
description = `Generate an image from a text prompt using a local image-generation backend. Supports Ollama image models (x/flux2-klein), Python Diffusers models (Sana 1.5 1.6B default, Sana 1.5 4.8B, Sana multilingual/2K/4K, FLUX.1 dev, SD3.5 Large, SDXL Turbo, Tiny-SD, LCM, Sana Sprint), and stable-diffusion.cpp local checkpoints/GGUF. When fallback is enabled, auto generation tries ranked high-quality candidates first (Sana 1.5 above FLUX so we avoid HF gating), including community FLUX mirrors, and then falls back to smaller models if setup, download, or generation fails. Aspect ratio and resolution are model-controllable: pass aspect_ratio (e.g. "16:9", "9:16", "4:3", "3:4", "1:1", "21:9", "2:3", "3:2") to derive width/height around the selected model's preferred base resolution, or pass explicit width/height (in pixels, both rounded to a multiple of 8) when a specific size is required. A preliminary prompt-expansion stage rewrites the user's prompt into a richer, model-tuned version before generation when an LLM expander is wired; pass expand_prompt=false to skip. Saves a PNG under .omnius/images and returns the file path.`;
|
|
261119
|
+
description = `Generate an image from a text prompt using a local image-generation backend. Supports Ollama image models (x/flux2-klein), Python Diffusers models (Sana 1.5 1.6B default, Sana 1.5 4.8B, Sana multilingual/2K/4K, FLUX.1 dev, FLUX adapter/LoRA presets, SD3.5 Large, SDXL Turbo, Tiny-SD, LCM, Sana Sprint), and stable-diffusion.cpp local checkpoints/GGUF. When fallback is enabled, auto generation tries ranked high-quality candidates first (Sana 1.5 above FLUX so we avoid HF gating), including community FLUX mirrors, and then falls back to smaller models if setup, download, or generation fails. Aspect ratio and resolution are model-controllable: pass aspect_ratio (e.g. "16:9", "9:16", "4:3", "3:4", "1:1", "21:9", "2:3", "3:2") to derive width/height around the selected model's preferred base resolution, or pass explicit width/height (in pixels, both rounded to a multiple of 8) when a specific size is required. A preliminary prompt-expansion stage rewrites the user's prompt into a richer, model-tuned version before generation when an LLM expander is wired; pass expand_prompt=false to skip. Saves a PNG under .omnius/images and returns the file path.`;
|
|
260917
261120
|
parameters = {
|
|
260918
261121
|
type: "object",
|
|
260919
261122
|
properties: {
|
|
@@ -261407,7 +261610,7 @@ ${errText.slice(0, 1200)}`,
|
|
|
261407
261610
|
try {
|
|
261408
261611
|
const space = ensureDiskSpaceForDownload({
|
|
261409
261612
|
approxDownloadBytes: approxBytes,
|
|
261410
|
-
keepRepos:
|
|
261613
|
+
keepRepos: imagePresetDependencyModels(preset, args.model)
|
|
261411
261614
|
});
|
|
261412
261615
|
if (space.evicted.length > 0) {
|
|
261413
261616
|
this.emitProgress({
|
|
@@ -261442,8 +261645,7 @@ ${errText.slice(0, 1200)}`,
|
|
|
261442
261645
|
}
|
|
261443
261646
|
const result = await runProcess2(python.command, [
|
|
261444
261647
|
runner,
|
|
261445
|
-
|
|
261446
|
-
args.model,
|
|
261648
|
+
...diffusersRunnerModelArgs(args.model),
|
|
261447
261649
|
"--prompt",
|
|
261448
261650
|
"omnius prewarm",
|
|
261449
261651
|
"--output",
|
|
@@ -261617,8 +261819,7 @@ ${errText.slice(0, 800)}`,
|
|
|
261617
261819
|
}
|
|
261618
261820
|
const argv = [
|
|
261619
261821
|
runner,
|
|
261620
|
-
|
|
261621
|
-
args.model,
|
|
261822
|
+
...diffusersRunnerModelArgs(args.model),
|
|
261622
261823
|
"--prompt",
|
|
261623
261824
|
args.prompt,
|
|
261624
261825
|
"--output",
|
|
@@ -261639,7 +261840,7 @@ ${errText.slice(0, 800)}`,
|
|
|
261639
261840
|
try {
|
|
261640
261841
|
const space = ensureDiskSpaceForDownload({
|
|
261641
261842
|
approxDownloadBytes: approxBytes,
|
|
261642
|
-
keepRepos:
|
|
261843
|
+
keepRepos: imagePresetDependencyModels(preset, args.model)
|
|
261643
261844
|
});
|
|
261644
261845
|
if (space.evicted.length > 0) {
|
|
261645
261846
|
this.emitProgress({
|
|
@@ -534669,6 +534870,7 @@ __export(dist_exports, {
|
|
|
534669
534870
|
createWorktree: () => createWorktree2,
|
|
534670
534871
|
defaultExposureForTool: () => defaultExposureForTool,
|
|
534671
534872
|
defaultExtensionForMime: () => defaultExtensionForMime,
|
|
534873
|
+
deleteCachedModel: () => deleteCachedModel,
|
|
534672
534874
|
deleteMediaModelAdapter: () => deleteMediaModelAdapter,
|
|
534673
534875
|
deleteTodos: () => deleteTodos,
|
|
534674
534876
|
detectCudaDevices: () => detectCudaDevices,
|
|
@@ -534733,6 +534935,7 @@ __export(dist_exports, {
|
|
|
534733
534935
|
imageGenerationDir: () => imageGenerationDir,
|
|
534734
534936
|
imageGenerationModelPresets: () => imageGenerationModelPresets,
|
|
534735
534937
|
imageGenerationSetupPlan: () => imageGenerationSetupPlan,
|
|
534938
|
+
imagePresetDependencyModels: () => imagePresetDependencyModels,
|
|
534736
534939
|
inferAudioGenerationBackend: () => inferAudioGenerationBackend,
|
|
534737
534940
|
inferImageGenerationBackend: () => inferImageGenerationBackend,
|
|
534738
534941
|
inferMediaBackend: () => inferMediaBackend,
|
|
@@ -586916,9 +587119,9 @@ var init_profiles = __esm({
|
|
|
586916
587119
|
encrypted: false,
|
|
586917
587120
|
created: "2026-03-31T00:00:00Z"
|
|
586918
587121
|
},
|
|
586919
|
-
"
|
|
586920
|
-
name: "
|
|
586921
|
-
description: "
|
|
587122
|
+
"bookkeeping-tracking": {
|
|
587123
|
+
name: "bookkeeping-tracking",
|
|
587124
|
+
description: "Bookkeeping only — todos, working notes, and completion markers. No filesystem, search, shell, network, or model-generation tools.",
|
|
586922
587125
|
tools: {
|
|
586923
587126
|
allow: ["todo_write", "todo_read", "working_notes", "task_complete"],
|
|
586924
587127
|
deny: [
|
|
@@ -616212,7 +616415,13 @@ function ollamaModelDiskStats(model, sizes) {
|
|
|
616212
616415
|
}
|
|
616213
616416
|
function imageModelDiskStats(ctx3, preset, ollamaSizes) {
|
|
616214
616417
|
if (preset.backend === "ollama") return ollamaModelDiskStats(preset.id, ollamaSizes);
|
|
616215
|
-
if (preset.backend === "diffusers")
|
|
616418
|
+
if (preset.backend === "diffusers") {
|
|
616419
|
+
const root = imageGenerationDir(ctx3.repoRoot);
|
|
616420
|
+
const parts = imagePresetDependencyModels(preset, preset.id).map((model) => cachedModelDiskStats(root, model));
|
|
616421
|
+
const paths = [...new Set(parts.flatMap((part) => part.paths))];
|
|
616422
|
+
const bytes = parts.reduce((sum, part) => sum + part.bytes, 0);
|
|
616423
|
+
return { downloaded: paths.length > 0, bytes, paths };
|
|
616424
|
+
}
|
|
616216
616425
|
return { downloaded: false, bytes: 0, paths: [] };
|
|
616217
616426
|
}
|
|
616218
616427
|
function audioModelDiskStats(ctx3, preset) {
|
|
@@ -616238,7 +616447,8 @@ async function deleteImageModelWeights(ctx3, preset) {
|
|
|
616238
616447
|
if (preset.backend === "ollama") {
|
|
616239
616448
|
messages2.push(await deleteOllamaWeights(ctx3, preset.id));
|
|
616240
616449
|
} else if (preset.backend === "diffusers") {
|
|
616241
|
-
const
|
|
616450
|
+
const root = imageGenerationDir(ctx3.repoRoot);
|
|
616451
|
+
const removed = imagePresetDependencyModels(preset, preset.id).flatMap((model) => removeCachedModelPaths(root, model));
|
|
616242
616452
|
messages2.push(removed.length > 0 ? `Deleted ${removed.length} cached image model path(s) for ${preset.id}.` : `No cached image weights found for ${preset.id}.`);
|
|
616243
616453
|
} else {
|
|
616244
616454
|
messages2.push("stable-diffusion.cpp uses explicit local checkpoint paths; remove the chosen checkpoint file directly if needed.");
|
|
@@ -616261,7 +616471,7 @@ async function showImageModelsMenu(ctx3, hasLocal) {
|
|
|
616261
616471
|
};
|
|
616262
616472
|
const items = [
|
|
616263
616473
|
{ key: "setup:ollama", label: "Setup Ollama", detail: "Pull x/z-image-turbo or x/flux2-klein" },
|
|
616264
|
-
{ key: "setup:diffusers", label: "Setup Diffusers", detail: "Auto-installs
|
|
616474
|
+
{ key: "setup:diffusers", label: "Setup Diffusers", detail: "Auto-installs the shared image runtime and selected Diffusers model" },
|
|
616265
616475
|
{ key: "setup:sdcpp", label: "Setup stable-diffusion.cpp", detail: "CPU/GGUF/checkpoint route" },
|
|
616266
616476
|
{ key: "hdr:models", label: selectColors.dim("─── Models ───") },
|
|
616267
616477
|
...imageGenerationModelPresets().map(buildModelItem)
|
|
@@ -630623,9 +630833,14 @@ function buildTelegramCommandMenuItems(scope) {
|
|
|
630623
630833
|
seen.add(cmd.name);
|
|
630624
630834
|
items.push({
|
|
630625
630835
|
label: `/${cmd.name}`,
|
|
630626
|
-
command: `/${cmd.name}`,
|
|
630627
630836
|
description: cmd.signatures[0]?.description ?? signature,
|
|
630628
|
-
adminOnly: scope === "admin"
|
|
630837
|
+
adminOnly: scope === "admin",
|
|
630838
|
+
action: {
|
|
630839
|
+
type: "command_detail",
|
|
630840
|
+
command: `/${cmd.name}`,
|
|
630841
|
+
label: `/${cmd.name}`,
|
|
630842
|
+
description: cmd.signatures.map((sig) => `${sig.signature} - ${sig.description}`).join("\n")
|
|
630843
|
+
}
|
|
630629
630844
|
});
|
|
630630
630845
|
}
|
|
630631
630846
|
return items.sort((a2, b) => a2.label.localeCompare(b.label));
|
|
@@ -630633,10 +630848,18 @@ function buildTelegramCommandMenuItems(scope) {
|
|
|
630633
630848
|
function buildTelegramGenerativeMenuItems(commandName) {
|
|
630634
630849
|
const name10 = commandName.replace(/^\//, "").toLowerCase();
|
|
630635
630850
|
if (!GENERATIVE_COMMANDS.has(name10)) return [];
|
|
630851
|
+
if (name10 === "models") {
|
|
630852
|
+
return [
|
|
630853
|
+
{ label: "CAD models", description: "Browse text-to-CAD adapters.", action: { type: "models", generation: "cad" } },
|
|
630854
|
+
{ label: "3D models", description: "Browse 3D mesh/reconstruction adapters.", action: { type: "models", generation: "model3d" } },
|
|
630855
|
+
{ label: "Model store", command: "/models", description: "Show unified model store status.", action: { type: "command", command: "/models" } }
|
|
630856
|
+
];
|
|
630857
|
+
}
|
|
630636
630858
|
const title = name10[0].toUpperCase() + name10.slice(1);
|
|
630859
|
+
const generation = name10 === "sound" ? "sound" : name10 === "music" ? "music" : name10;
|
|
630637
630860
|
return [
|
|
630638
|
-
{ label: `${title} models`,
|
|
630639
|
-
{ label: `${title} setup`,
|
|
630861
|
+
{ label: `${title} models`, description: `Browse selectable ${name10} models, metadata, cache state, and actions.`, action: { type: "models", generation } },
|
|
630862
|
+
{ label: `${title} setup`, description: `Show setup commands for the ${name10} backend.`, action: { type: "setup_generation", generation } }
|
|
630640
630863
|
];
|
|
630641
630864
|
}
|
|
630642
630865
|
function encodeTelegramCommandMenuCallback(action, value2) {
|
|
@@ -630646,43 +630869,85 @@ function encodeTelegramCommandMenuCallback(action, value2) {
|
|
|
630646
630869
|
function decodeTelegramCommandMenuCallback(data) {
|
|
630647
630870
|
const parts = data.split(":");
|
|
630648
630871
|
if (parts.length !== 3 || parts[0] !== CALLBACK_PREFIX2) return null;
|
|
630649
|
-
const action = parts[1] === "p" ? "page" : parts[1] === "r" ? "run" : parts[1] === "c" ? "close" : null;
|
|
630872
|
+
const action = parts[1] === "p" ? "page" : parts[1] === "r" ? "run" : parts[1] === "c" ? "close" : parts[1] === "b" ? "back" : null;
|
|
630650
630873
|
if (!action) return null;
|
|
630651
630874
|
return { action, value: parts[2] ?? "" };
|
|
630652
630875
|
}
|
|
630876
|
+
function pushTelegramCommandMenuState(state, next) {
|
|
630877
|
+
const crumb = {
|
|
630878
|
+
kind: state.kind,
|
|
630879
|
+
title: state.title,
|
|
630880
|
+
subtitle: state.subtitle,
|
|
630881
|
+
body: state.body,
|
|
630882
|
+
page: state.page,
|
|
630883
|
+
items: state.items
|
|
630884
|
+
};
|
|
630885
|
+
return {
|
|
630886
|
+
...state,
|
|
630887
|
+
kind: next.kind ?? state.kind,
|
|
630888
|
+
title: next.title,
|
|
630889
|
+
subtitle: next.subtitle,
|
|
630890
|
+
body: next.body,
|
|
630891
|
+
page: next.page ?? 0,
|
|
630892
|
+
items: next.items,
|
|
630893
|
+
breadcrumbs: [...state.breadcrumbs ?? [], crumb]
|
|
630894
|
+
};
|
|
630895
|
+
}
|
|
630896
|
+
function popTelegramCommandMenuState(state) {
|
|
630897
|
+
const stack = state.breadcrumbs ?? [];
|
|
630898
|
+
const previous = stack.at(-1);
|
|
630899
|
+
if (!previous) return null;
|
|
630900
|
+
return {
|
|
630901
|
+
...state,
|
|
630902
|
+
kind: previous.kind,
|
|
630903
|
+
title: previous.title,
|
|
630904
|
+
subtitle: previous.subtitle,
|
|
630905
|
+
body: previous.body,
|
|
630906
|
+
page: previous.page,
|
|
630907
|
+
items: previous.items,
|
|
630908
|
+
breadcrumbs: stack.slice(0, -1)
|
|
630909
|
+
};
|
|
630910
|
+
}
|
|
630653
630911
|
function renderTelegramCommandMenu(state) {
|
|
630654
630912
|
const totalPages = Math.max(1, Math.ceil(state.items.length / PAGE_SIZE2));
|
|
630655
630913
|
const page2 = Math.max(0, Math.min(state.page, totalPages - 1));
|
|
630656
630914
|
const start2 = page2 * PAGE_SIZE2;
|
|
630657
630915
|
const visible = state.items.slice(start2, start2 + PAGE_SIZE2);
|
|
630658
|
-
const title = state.kind === "generative" ? "Generative command" : "Commands";
|
|
630916
|
+
const title = state.title || (state.kind === "generative" ? "Generative command" : state.kind === "models" ? "Models" : "Commands");
|
|
630659
630917
|
const scope = state.scope === "admin" ? "admin" : "public";
|
|
630660
630918
|
const lines = [
|
|
630661
630919
|
`<b>${escapeHTML3(title)}</b>`,
|
|
630662
630920
|
`<i>${escapeHTML3(scope)} scope - page ${page2 + 1}/${totalPages}</i>`,
|
|
630921
|
+
state.subtitle ? escapeHTML3(state.subtitle) : "",
|
|
630922
|
+
state.body ? `<blockquote expandable>${escapeHTML3(state.body)}</blockquote>` : "",
|
|
630663
630923
|
"",
|
|
630664
630924
|
...visible.flatMap((item) => [
|
|
630665
|
-
`<code>${escapeHTML3(item.command)}</code>`,
|
|
630925
|
+
item.command ? `<code>${escapeHTML3(item.command)}</code>` : `<b>${escapeHTML3(item.label)}</b>`,
|
|
630666
630926
|
escapeHTML3(item.description)
|
|
630667
630927
|
])
|
|
630668
|
-
];
|
|
630928
|
+
].filter((line) => line !== "");
|
|
630669
630929
|
const keyboard = visible.map((item, offset) => [{
|
|
630670
630930
|
text: item.label.slice(0, 32),
|
|
630671
630931
|
callback_data: encodeTelegramCommandMenuCallback("run", start2 + offset)
|
|
630672
630932
|
}]);
|
|
630673
630933
|
const nav = [];
|
|
630674
630934
|
nav.push({ text: "Close", callback_data: encodeTelegramCommandMenuCallback("close", 0) });
|
|
630935
|
+
if ((state.breadcrumbs ?? []).length > 0) nav.push({ text: "Back", callback_data: encodeTelegramCommandMenuCallback("back", 0) });
|
|
630675
630936
|
if (page2 > 0) nav.push({ text: "Prev", callback_data: encodeTelegramCommandMenuCallback("page", page2 - 1) });
|
|
630676
630937
|
nav.push({ text: `${page2 + 1}/${totalPages}`, callback_data: encodeTelegramCommandMenuCallback("page", page2) });
|
|
630677
630938
|
if (page2 < totalPages - 1) nav.push({ text: "Next", callback_data: encodeTelegramCommandMenuCallback("page", page2 + 1) });
|
|
630678
630939
|
keyboard.push(nav);
|
|
630679
|
-
return { text: lines.join("\n"), reply_markup: { inline_keyboard: keyboard } };
|
|
630940
|
+
return { text: truncateTelegramMenuText(lines.join("\n")), reply_markup: { inline_keyboard: keyboard } };
|
|
630680
630941
|
}
|
|
630681
630942
|
function handleTelegramCommandMenuCallback(data, state, now = Date.now()) {
|
|
630682
630943
|
const decoded = decodeTelegramCommandMenuCallback(data);
|
|
630683
630944
|
if (!decoded) return null;
|
|
630684
630945
|
if (state.expiresAt <= now) return null;
|
|
630685
630946
|
if (decoded.action === "close") return { close: true };
|
|
630947
|
+
if (decoded.action === "back") {
|
|
630948
|
+
const newState = popTelegramCommandMenuState(state);
|
|
630949
|
+
return newState ? { newState, render: renderTelegramCommandMenu(newState) } : null;
|
|
630950
|
+
}
|
|
630686
630951
|
if (decoded.action === "page") {
|
|
630687
630952
|
const totalPages = Math.max(1, Math.ceil(state.items.length / PAGE_SIZE2));
|
|
630688
630953
|
const page2 = Math.max(0, Math.min(Number.parseInt(decoded.value, 10) || 0, totalPages - 1));
|
|
@@ -630691,21 +630956,28 @@ function handleTelegramCommandMenuCallback(data, state, now = Date.now()) {
|
|
|
630691
630956
|
}
|
|
630692
630957
|
const index = Number.parseInt(decoded.value, 10);
|
|
630693
630958
|
const item = Number.isFinite(index) ? state.items[index] : void 0;
|
|
630694
|
-
|
|
630959
|
+
if (!item) return null;
|
|
630960
|
+
return item.command ? { command: item.command, item } : { item };
|
|
630695
630961
|
}
|
|
630696
630962
|
function escapeHTML3(text) {
|
|
630697
630963
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
630698
630964
|
}
|
|
630965
|
+
function truncateTelegramMenuText(text) {
|
|
630966
|
+
if (text.length <= 3900) return text;
|
|
630967
|
+
return `${text.slice(0, 3820)}
|
|
630968
|
+
|
|
630969
|
+
<i>... menu page clipped; use buttons to narrow this view.</i>`;
|
|
630970
|
+
}
|
|
630699
630971
|
var CALLBACK_PREFIX2, PAGE_SIZE2, TTL_MS, MAX_CALLBACK_DATA_BYTES, GENERATIVE_COMMANDS, TelegramCommandMenuStateStore;
|
|
630700
630972
|
var init_telegram_command_menu = __esm({
|
|
630701
630973
|
"packages/cli/src/tui/telegram-command-menu.ts"() {
|
|
630702
630974
|
"use strict";
|
|
630703
630975
|
init_command_registry();
|
|
630704
630976
|
CALLBACK_PREFIX2 = "ocm";
|
|
630705
|
-
PAGE_SIZE2 =
|
|
630977
|
+
PAGE_SIZE2 = 7;
|
|
630706
630978
|
TTL_MS = 10 * 60 * 1e3;
|
|
630707
630979
|
MAX_CALLBACK_DATA_BYTES = 64;
|
|
630708
|
-
GENERATIVE_COMMANDS = /* @__PURE__ */ new Set(["image", "video", "sound", "music"]);
|
|
630980
|
+
GENERATIVE_COMMANDS = /* @__PURE__ */ new Set(["image", "video", "sound", "music", "models"]);
|
|
630709
630981
|
TelegramCommandMenuStateStore = class {
|
|
630710
630982
|
states = /* @__PURE__ */ new Map();
|
|
630711
630983
|
key(chatId, messageId) {
|
|
@@ -630714,6 +630986,7 @@ var init_telegram_command_menu = __esm({
|
|
|
630714
630986
|
create(input, now = Date.now()) {
|
|
630715
630987
|
return {
|
|
630716
630988
|
...input,
|
|
630989
|
+
breadcrumbs: input.breadcrumbs ?? [],
|
|
630717
630990
|
createdAt: now,
|
|
630718
630991
|
expiresAt: now + TTL_MS
|
|
630719
630992
|
};
|
|
@@ -634165,6 +634438,17 @@ import { join as join140, resolve as resolve53, basename as basename35, relative
|
|
|
634165
634438
|
import { homedir as homedir44 } from "node:os";
|
|
634166
634439
|
import { writeFile as writeFileAsync } from "node:fs/promises";
|
|
634167
634440
|
import { createHash as createHash33, randomBytes as randomBytes25, randomInt } from "node:crypto";
|
|
634441
|
+
function formatModelBytes(bytes) {
|
|
634442
|
+
if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
|
|
634443
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
634444
|
+
let value2 = bytes;
|
|
634445
|
+
let unit = 0;
|
|
634446
|
+
while (value2 >= 1024 && unit < units.length - 1) {
|
|
634447
|
+
value2 /= 1024;
|
|
634448
|
+
unit++;
|
|
634449
|
+
}
|
|
634450
|
+
return `${value2 >= 10 || unit === 0 ? value2.toFixed(0) : value2.toFixed(1)} ${units[unit]}`;
|
|
634451
|
+
}
|
|
634168
634452
|
function cleanTelegramDecisionNote(value2, maxLength = 260) {
|
|
634169
634453
|
if (typeof value2 !== "string") return void 0;
|
|
634170
634454
|
const clean5 = stripTelegramHiddenThinking(value2).replace(/\s+/g, " ").trim();
|
|
@@ -636853,6 +637137,8 @@ Telegram link integrity contract:
|
|
|
636853
637137
|
statsMenuPruneTimer = null;
|
|
636854
637138
|
/** Telegram-native command and generative command menus */
|
|
636855
637139
|
telegramCommandMenuStates = new TelegramCommandMenuStateStore();
|
|
637140
|
+
/** One-shot Telegram prompt intents created by model-menu "Generate" buttons. */
|
|
637141
|
+
telegramGenerationPromptIntents = /* @__PURE__ */ new Map();
|
|
636856
637142
|
/** Command handler for admin DM slash commands (wired from interactive.ts) */
|
|
636857
637143
|
commandHandler = null;
|
|
636858
637144
|
/** Callback fired after a Telegram user completes the TUI-only admin auth challenge */
|
|
@@ -637790,7 +638076,7 @@ ${message2}`)
|
|
|
637790
638076
|
return;
|
|
637791
638077
|
}
|
|
637792
638078
|
if (msg.guestQueryId || !isAdmin) {
|
|
637793
|
-
const lines = items.slice(0, 24).map((item) => `${item.command} - ${item.description}`);
|
|
638079
|
+
const lines = items.slice(0, 24).map((item) => `${item.command ?? item.label} - ${item.description}`);
|
|
637794
638080
|
const text = ["Available commands:", "", ...lines].join("\n");
|
|
637795
638081
|
if (msg.guestQueryId) {
|
|
637796
638082
|
await this.answerGuestQuery(msg.guestQueryId, text);
|
|
@@ -637806,9 +638092,14 @@ ${message2}`)
|
|
|
637806
638092
|
fromUserId: msg.fromUserId ?? 0,
|
|
637807
638093
|
scope,
|
|
637808
638094
|
kind,
|
|
638095
|
+
title: kind === "generative" ? `/${commandName ?? "models"}` : void 0,
|
|
638096
|
+
subtitle: kind === "generative" ? "Telegram-native menu; buttons render data and tools directly here." : void 0,
|
|
637809
638097
|
page: 0,
|
|
637810
638098
|
items
|
|
637811
638099
|
});
|
|
638100
|
+
await this.sendTelegramCommandMenuState(msg, previewState);
|
|
638101
|
+
}
|
|
638102
|
+
async sendTelegramCommandMenuState(msg, previewState) {
|
|
637812
638103
|
const menu = renderTelegramCommandMenu(previewState);
|
|
637813
638104
|
const sent = await this.apiCall("sendMessage", {
|
|
637814
638105
|
chat_id: msg.chatId,
|
|
@@ -637825,6 +638116,643 @@ ${message2}`)
|
|
|
637825
638116
|
});
|
|
637826
638117
|
}
|
|
637827
638118
|
}
|
|
638119
|
+
async editTelegramCommandMenuState(chatId, messageId, state) {
|
|
638120
|
+
const render2 = renderTelegramCommandMenu(state);
|
|
638121
|
+
this.telegramCommandMenuStates.set(state);
|
|
638122
|
+
await this.apiCall("editMessageText", {
|
|
638123
|
+
chat_id: chatId,
|
|
638124
|
+
message_id: messageId,
|
|
638125
|
+
text: render2.text,
|
|
638126
|
+
parse_mode: "HTML",
|
|
638127
|
+
reply_markup: JSON.stringify(render2.reply_markup)
|
|
638128
|
+
});
|
|
638129
|
+
}
|
|
638130
|
+
telegramGenerationIntentKey(chatId, fromUserId) {
|
|
638131
|
+
return `${String(chatId)}:${fromUserId}`;
|
|
638132
|
+
}
|
|
638133
|
+
pruneTelegramGenerationPromptIntents(now = Date.now()) {
|
|
638134
|
+
for (const [key, intent] of this.telegramGenerationPromptIntents) {
|
|
638135
|
+
if (intent.expiresAt <= now) this.telegramGenerationPromptIntents.delete(key);
|
|
638136
|
+
}
|
|
638137
|
+
}
|
|
638138
|
+
telegramGenerationLabel(generation) {
|
|
638139
|
+
if (generation === "model3d") return "3D model";
|
|
638140
|
+
if (generation === "cad") return "CAD";
|
|
638141
|
+
return generation;
|
|
638142
|
+
}
|
|
638143
|
+
telegramModelCachedBytes(dependencies) {
|
|
638144
|
+
return dependencies.reduce((sum, dep) => sum + measureRepoCacheBytes(dep), 0);
|
|
638145
|
+
}
|
|
638146
|
+
telegramImageModelDescriptors() {
|
|
638147
|
+
return imageGenerationModelPresets().map((preset) => {
|
|
638148
|
+
const dependencies = imagePresetDependencyModels(preset, preset.id);
|
|
638149
|
+
return {
|
|
638150
|
+
generation: "image",
|
|
638151
|
+
id: preset.id,
|
|
638152
|
+
label: preset.label,
|
|
638153
|
+
backend: preset.backend,
|
|
638154
|
+
category: preset.category ?? "Image",
|
|
638155
|
+
sizeClass: preset.sizeClass ?? "",
|
|
638156
|
+
quality: preset.quality ?? preset.note,
|
|
638157
|
+
note: preset.note,
|
|
638158
|
+
install: preset.install,
|
|
638159
|
+
minVramGB: preset.minVramGB,
|
|
638160
|
+
recommendedVramGB: preset.recommendedVramGB,
|
|
638161
|
+
approxDownloadGB: preset.approxDownloadGB,
|
|
638162
|
+
dependencies,
|
|
638163
|
+
cachedBytes: this.telegramModelCachedBytes(dependencies)
|
|
638164
|
+
};
|
|
638165
|
+
});
|
|
638166
|
+
}
|
|
638167
|
+
telegramAudioModelDescriptors(kind) {
|
|
638168
|
+
return audioGenerationModelPresets(kind).map((preset) => {
|
|
638169
|
+
const dependencies = [preset.id];
|
|
638170
|
+
return {
|
|
638171
|
+
generation: kind,
|
|
638172
|
+
id: preset.id,
|
|
638173
|
+
label: preset.label,
|
|
638174
|
+
backend: preset.backend,
|
|
638175
|
+
category: preset.category,
|
|
638176
|
+
sizeClass: preset.sizeClass,
|
|
638177
|
+
quality: preset.quality,
|
|
638178
|
+
note: preset.note,
|
|
638179
|
+
install: preset.install,
|
|
638180
|
+
minVramGB: preset.minVramGB,
|
|
638181
|
+
recommendedVramGB: preset.recommendedVramGB,
|
|
638182
|
+
approxDownloadGB: preset.approxDownloadGB,
|
|
638183
|
+
dependencies,
|
|
638184
|
+
cachedBytes: this.telegramModelCachedBytes(dependencies)
|
|
638185
|
+
};
|
|
638186
|
+
});
|
|
638187
|
+
}
|
|
638188
|
+
telegramVideoModelDescriptors() {
|
|
638189
|
+
return videoGenerationModelPresets().map((preset) => {
|
|
638190
|
+
const dependencies = [preset.id];
|
|
638191
|
+
return {
|
|
638192
|
+
generation: "video",
|
|
638193
|
+
id: preset.id,
|
|
638194
|
+
label: preset.label,
|
|
638195
|
+
backend: preset.backend,
|
|
638196
|
+
category: preset.category,
|
|
638197
|
+
sizeClass: preset.sizeClass,
|
|
638198
|
+
quality: preset.quality,
|
|
638199
|
+
note: preset.note,
|
|
638200
|
+
install: preset.install,
|
|
638201
|
+
minVramGB: preset.minVramGB,
|
|
638202
|
+
recommendedVramGB: preset.recommendedVramGB,
|
|
638203
|
+
approxDownloadGB: preset.approxDownloadGB,
|
|
638204
|
+
dependencies,
|
|
638205
|
+
cachedBytes: this.telegramModelCachedBytes(dependencies),
|
|
638206
|
+
unavailableReason: preset.gated ? "Requires Hugging Face token/license acceptance." : void 0
|
|
638207
|
+
};
|
|
638208
|
+
});
|
|
638209
|
+
}
|
|
638210
|
+
telegramCad3dModelDescriptors(generation) {
|
|
638211
|
+
const modality = generation === "cad" ? "cad" : "3d";
|
|
638212
|
+
return listMediaModelCatalog(modality).map((entry) => {
|
|
638213
|
+
const spec = entry.spec;
|
|
638214
|
+
const dependencies = [spec.repoId];
|
|
638215
|
+
const notes = spec.deployment.notes.join(" ");
|
|
638216
|
+
return {
|
|
638217
|
+
generation,
|
|
638218
|
+
id: spec.id,
|
|
638219
|
+
label: spec.label || spec.repoId,
|
|
638220
|
+
backend: spec.backend,
|
|
638221
|
+
category: `${spec.modality}/${spec.status}`,
|
|
638222
|
+
sizeClass: spec.resources.approxDownloadGB ? `~${spec.resources.approxDownloadGB}GB download` : spec.runner,
|
|
638223
|
+
quality: spec.hf.cardExcerpt || notes || spec.runner,
|
|
638224
|
+
note: notes || spec.deployment.install,
|
|
638225
|
+
install: spec.deployment.install,
|
|
638226
|
+
minVramGB: spec.resources.minVramGB,
|
|
638227
|
+
recommendedVramGB: spec.resources.recommendedVramGB,
|
|
638228
|
+
approxDownloadGB: spec.resources.approxDownloadGB,
|
|
638229
|
+
dependencies,
|
|
638230
|
+
cachedBytes: this.telegramModelCachedBytes(dependencies),
|
|
638231
|
+
unavailableReason: spec.deployment.runtimeCompatible ? void 0 : "Catalog metadata exists, but the runtime adapter is not wired for artifact creation."
|
|
638232
|
+
};
|
|
638233
|
+
});
|
|
638234
|
+
}
|
|
638235
|
+
telegramGenerationModelDescriptors(generation) {
|
|
638236
|
+
if (generation === "image") return this.telegramImageModelDescriptors();
|
|
638237
|
+
if (generation === "sound" || generation === "music") return this.telegramAudioModelDescriptors(generation);
|
|
638238
|
+
if (generation === "video") return this.telegramVideoModelDescriptors();
|
|
638239
|
+
return this.telegramCad3dModelDescriptors(generation);
|
|
638240
|
+
}
|
|
638241
|
+
telegramModelDescriptor(generation, id) {
|
|
638242
|
+
return this.telegramGenerationModelDescriptors(generation).find((model) => model.id === id);
|
|
638243
|
+
}
|
|
638244
|
+
telegramModelMenuItems(generation) {
|
|
638245
|
+
return this.telegramGenerationModelDescriptors(generation).map((model) => {
|
|
638246
|
+
const cached = model.cachedBytes > 0 ? `cached ${formatModelBytes(model.cachedBytes)}` : "not downloaded";
|
|
638247
|
+
const fit3 = model.recommendedVramGB ? `${model.recommendedVramGB}GB VRAM rec` : model.minVramGB ? `${model.minVramGB}GB VRAM min` : "VRAM n/a";
|
|
638248
|
+
return {
|
|
638249
|
+
label: model.label,
|
|
638250
|
+
description: `${model.backend} - ${model.category} - ${fit3} - ${cached}`,
|
|
638251
|
+
action: {
|
|
638252
|
+
type: "model_detail",
|
|
638253
|
+
generation,
|
|
638254
|
+
model: model.id
|
|
638255
|
+
}
|
|
638256
|
+
};
|
|
638257
|
+
});
|
|
638258
|
+
}
|
|
638259
|
+
telegramModelDetailBody(model) {
|
|
638260
|
+
const cached = model.cachedBytes > 0 ? `${formatModelBytes(model.cachedBytes)} cached` : "not downloaded";
|
|
638261
|
+
const deps = model.dependencies.length > 0 ? model.dependencies.join(", ") : model.id;
|
|
638262
|
+
return [
|
|
638263
|
+
`id: ${model.id}`,
|
|
638264
|
+
`backend: ${model.backend}`,
|
|
638265
|
+
`category: ${model.category}`,
|
|
638266
|
+
`size: ${model.sizeClass || "unknown"}`,
|
|
638267
|
+
`download: ${model.approxDownloadGB ? `~${model.approxDownloadGB}GB` : "unknown"}; cache: ${cached}`,
|
|
638268
|
+
`vram: min ${model.minVramGB ?? "?"}GB; recommended ${model.recommendedVramGB ?? "?"}GB`,
|
|
638269
|
+
`dependencies: ${deps}`,
|
|
638270
|
+
model.unavailableReason ? `runtime note: ${model.unavailableReason}` : "",
|
|
638271
|
+
"",
|
|
638272
|
+
`quality: ${model.quality}`,
|
|
638273
|
+
"",
|
|
638274
|
+
`note: ${model.note}`,
|
|
638275
|
+
model.install ? `
|
|
638276
|
+
install/prewarm: ${model.install}` : ""
|
|
638277
|
+
].filter(Boolean).join("\n");
|
|
638278
|
+
}
|
|
638279
|
+
telegramModelDetailItems(model) {
|
|
638280
|
+
return [
|
|
638281
|
+
{
|
|
638282
|
+
label: "Generate",
|
|
638283
|
+
description: "Use this model for the next Telegram message you type.",
|
|
638284
|
+
action: { type: "await_prompt", generation: model.generation, model: model.id, backend: model.backend, label: model.label }
|
|
638285
|
+
},
|
|
638286
|
+
{
|
|
638287
|
+
label: "Load",
|
|
638288
|
+
description: "Download/prewarm or check this model in the generation runtime.",
|
|
638289
|
+
action: { type: "prewarm_model", generation: model.generation, model: model.id, backend: model.backend, label: model.label }
|
|
638290
|
+
},
|
|
638291
|
+
{
|
|
638292
|
+
label: "Delete",
|
|
638293
|
+
description: "Remove cached weights for this model and its adapter/base dependencies.",
|
|
638294
|
+
action: { type: "delete_model", generation: model.generation, model: model.id, backend: model.backend, label: model.label }
|
|
638295
|
+
}
|
|
638296
|
+
];
|
|
638297
|
+
}
|
|
638298
|
+
async replyWithTelegramModelBrowser(msg, isAdmin, generation) {
|
|
638299
|
+
if (!isAdmin) {
|
|
638300
|
+
await this.replyToTelegramMessage(msg, "Model menus require Telegram admin authentication.");
|
|
638301
|
+
return;
|
|
638302
|
+
}
|
|
638303
|
+
const scope = "admin";
|
|
638304
|
+
const title = `${this.telegramGenerationLabel(generation)} models`;
|
|
638305
|
+
const cachedCount = listCachedModels().length;
|
|
638306
|
+
const previewState = this.telegramCommandMenuStates.create({
|
|
638307
|
+
chatId: msg.chatId,
|
|
638308
|
+
messageId: 0,
|
|
638309
|
+
invokerMessageId: msg.messageId,
|
|
638310
|
+
fromUserId: msg.fromUserId ?? 0,
|
|
638311
|
+
scope,
|
|
638312
|
+
kind: "models",
|
|
638313
|
+
title,
|
|
638314
|
+
subtitle: `${cachedCount} cached model record(s). Select a model for metadata, load, delete, or generate.`,
|
|
638315
|
+
page: 0,
|
|
638316
|
+
items: this.telegramModelMenuItems(generation)
|
|
638317
|
+
});
|
|
638318
|
+
await this.sendTelegramCommandMenuState(msg, previewState);
|
|
638319
|
+
}
|
|
638320
|
+
parseTelegramGenerationMenuKind(value2) {
|
|
638321
|
+
const raw = String(value2 ?? "").toLowerCase();
|
|
638322
|
+
if (raw === "image" || raw === "sound" || raw === "music" || raw === "video" || raw === "cad") return raw;
|
|
638323
|
+
if (raw === "model3d" || raw === "3d" || raw === "model") return "model3d";
|
|
638324
|
+
return null;
|
|
638325
|
+
}
|
|
638326
|
+
async handleTelegramCommandMenuAction(callback, state, action) {
|
|
638327
|
+
if (!action) return { handled: false };
|
|
638328
|
+
const chatId = callback.chatId;
|
|
638329
|
+
const messageId = callback.messageId;
|
|
638330
|
+
if (!chatId || !messageId) return { handled: true, answerText: "Cannot identify menu message.", alert: true };
|
|
638331
|
+
if (action.type === "command_detail") {
|
|
638332
|
+
const command = typeof action.command === "string" ? action.command : "";
|
|
638333
|
+
if (!command) return { handled: true, answerText: "Command metadata is missing.", alert: true };
|
|
638334
|
+
const body = [
|
|
638335
|
+
`command: ${command}`,
|
|
638336
|
+
"",
|
|
638337
|
+
typeof action.description === "string" ? action.description : "No command details available."
|
|
638338
|
+
].join("\n");
|
|
638339
|
+
const nextState = pushTelegramCommandMenuState(state, {
|
|
638340
|
+
kind: "detail",
|
|
638341
|
+
title: command,
|
|
638342
|
+
subtitle: "Telegram command",
|
|
638343
|
+
body,
|
|
638344
|
+
items: [{
|
|
638345
|
+
label: "Run",
|
|
638346
|
+
command,
|
|
638347
|
+
description: `Execute ${command}.`,
|
|
638348
|
+
action: { type: "command", command }
|
|
638349
|
+
}]
|
|
638350
|
+
});
|
|
638351
|
+
await this.editTelegramCommandMenuState(chatId, messageId, nextState);
|
|
638352
|
+
return { handled: true };
|
|
638353
|
+
}
|
|
638354
|
+
if (action.type === "models") {
|
|
638355
|
+
const generation = this.parseTelegramGenerationMenuKind(action.generation);
|
|
638356
|
+
if (!generation) return { handled: true, answerText: "Unknown model menu.", alert: true };
|
|
638357
|
+
const nextState = pushTelegramCommandMenuState(state, {
|
|
638358
|
+
kind: "models",
|
|
638359
|
+
title: `${this.telegramGenerationLabel(generation)} models`,
|
|
638360
|
+
subtitle: "Select a model for metadata, load, delete, or generate.",
|
|
638361
|
+
items: this.telegramModelMenuItems(generation)
|
|
638362
|
+
});
|
|
638363
|
+
await this.editTelegramCommandMenuState(chatId, messageId, nextState);
|
|
638364
|
+
return { handled: true };
|
|
638365
|
+
}
|
|
638366
|
+
if (action.type === "setup_generation") {
|
|
638367
|
+
const generation = this.parseTelegramGenerationMenuKind(action.generation);
|
|
638368
|
+
if (!generation) return { handled: true, answerText: "Unknown setup menu.", alert: true };
|
|
638369
|
+
await this.answerCallbackQuery(callback.id, `Preparing ${this.telegramGenerationLabel(generation)} setup...`).catch(() => false);
|
|
638370
|
+
await this.replyWithTelegramGenerationSetup({
|
|
638371
|
+
chatId,
|
|
638372
|
+
text: "",
|
|
638373
|
+
username: callback.username,
|
|
638374
|
+
firstName: callback.firstName,
|
|
638375
|
+
messageId,
|
|
638376
|
+
fromUserId: callback.fromUserId,
|
|
638377
|
+
chatType: "private"
|
|
638378
|
+
}, generation);
|
|
638379
|
+
return { handled: true, answered: true };
|
|
638380
|
+
}
|
|
638381
|
+
if (action.type === "model_detail") {
|
|
638382
|
+
const generation = this.parseTelegramGenerationMenuKind(action.generation);
|
|
638383
|
+
const modelId = typeof action.model === "string" ? action.model : "";
|
|
638384
|
+
const descriptor = generation ? this.telegramModelDescriptor(generation, modelId) : void 0;
|
|
638385
|
+
if (!generation || !descriptor) return { handled: true, answerText: "Model metadata is no longer available.", alert: true };
|
|
638386
|
+
const nextState = pushTelegramCommandMenuState(state, {
|
|
638387
|
+
kind: "detail",
|
|
638388
|
+
title: descriptor.label,
|
|
638389
|
+
subtitle: `${this.telegramGenerationLabel(generation)} model`,
|
|
638390
|
+
body: this.telegramModelDetailBody(descriptor),
|
|
638391
|
+
items: this.telegramModelDetailItems(descriptor)
|
|
638392
|
+
});
|
|
638393
|
+
await this.editTelegramCommandMenuState(chatId, messageId, nextState);
|
|
638394
|
+
return { handled: true };
|
|
638395
|
+
}
|
|
638396
|
+
if (action.type === "await_prompt") {
|
|
638397
|
+
const generation = this.parseTelegramGenerationMenuKind(action.generation);
|
|
638398
|
+
const modelId = typeof action.model === "string" ? action.model : "";
|
|
638399
|
+
const descriptor = generation ? this.telegramModelDescriptor(generation, modelId) : void 0;
|
|
638400
|
+
if (!generation || !descriptor) return { handled: true, answerText: "Model metadata is no longer available.", alert: true };
|
|
638401
|
+
const now = Date.now();
|
|
638402
|
+
this.pruneTelegramGenerationPromptIntents(now);
|
|
638403
|
+
this.telegramGenerationPromptIntents.set(this.telegramGenerationIntentKey(chatId, callback.fromUserId), {
|
|
638404
|
+
chatId,
|
|
638405
|
+
chatType: "private",
|
|
638406
|
+
fromUserId: callback.fromUserId,
|
|
638407
|
+
username: callback.username,
|
|
638408
|
+
generation,
|
|
638409
|
+
model: descriptor.id,
|
|
638410
|
+
backend: descriptor.backend,
|
|
638411
|
+
label: descriptor.label,
|
|
638412
|
+
createdAt: now,
|
|
638413
|
+
expiresAt: now + 15 * 6e4,
|
|
638414
|
+
menuMessageId: messageId
|
|
638415
|
+
});
|
|
638416
|
+
const nextState = {
|
|
638417
|
+
...state,
|
|
638418
|
+
subtitle: "Waiting for your next Telegram message.",
|
|
638419
|
+
body: [
|
|
638420
|
+
`Selected: ${descriptor.label}`,
|
|
638421
|
+
`Model: ${descriptor.id}`,
|
|
638422
|
+
"",
|
|
638423
|
+
`Send the prompt you want to generate. Omnius will expand it first, then run the ${this.telegramGenerationLabel(generation)} pipeline with this model.`,
|
|
638424
|
+
"Send /cancel to clear this pending generation."
|
|
638425
|
+
].join("\n")
|
|
638426
|
+
};
|
|
638427
|
+
await this.editTelegramCommandMenuState(chatId, messageId, nextState);
|
|
638428
|
+
return { handled: true, answerText: "Send the prompt as your next Telegram message." };
|
|
638429
|
+
}
|
|
638430
|
+
if (action.type === "prewarm_model") {
|
|
638431
|
+
const generation = this.parseTelegramGenerationMenuKind(action.generation);
|
|
638432
|
+
const modelId = typeof action.model === "string" ? action.model : "";
|
|
638433
|
+
const descriptor = generation ? this.telegramModelDescriptor(generation, modelId) : void 0;
|
|
638434
|
+
if (!generation || !descriptor) return { handled: true, answerText: "Model metadata is no longer available.", alert: true };
|
|
638435
|
+
await this.answerCallbackQuery(callback.id, `Loading ${descriptor.label}...`).catch(() => false);
|
|
638436
|
+
await this.runTelegramGenerationPrewarm(chatId, descriptor);
|
|
638437
|
+
return { handled: true, answered: true };
|
|
638438
|
+
}
|
|
638439
|
+
if (action.type === "delete_model") {
|
|
638440
|
+
const generation = this.parseTelegramGenerationMenuKind(action.generation);
|
|
638441
|
+
const modelId = typeof action.model === "string" ? action.model : "";
|
|
638442
|
+
const descriptor = generation ? this.telegramModelDescriptor(generation, modelId) : void 0;
|
|
638443
|
+
if (!generation || !descriptor) return { handled: true, answerText: "Model metadata is no longer available.", alert: true };
|
|
638444
|
+
const message2 = await this.deleteTelegramGenerationModelWeights(descriptor);
|
|
638445
|
+
await this.sendMessageHTML(chatId, convertMarkdownToTelegramHTML(message2));
|
|
638446
|
+
return { handled: true, answerText: "Delete complete." };
|
|
638447
|
+
}
|
|
638448
|
+
return { handled: false };
|
|
638449
|
+
}
|
|
638450
|
+
async deleteTelegramGenerationModelWeights(model) {
|
|
638451
|
+
if (model.backend === "ollama" || model.id.startsWith("x/")) {
|
|
638452
|
+
const base3 = String(this.agentConfig?.backendUrl || "http://localhost:11434").replace(/\/v1\/?$/, "").replace(/\/$/, "");
|
|
638453
|
+
const resp = await fetch(`${base3}/api/delete`, {
|
|
638454
|
+
method: "DELETE",
|
|
638455
|
+
headers: { "Content-Type": "application/json" },
|
|
638456
|
+
body: JSON.stringify({ name: model.id }),
|
|
638457
|
+
signal: AbortSignal.timeout(3e4)
|
|
638458
|
+
}).catch((err) => {
|
|
638459
|
+
throw new Error(`Ollama delete failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
638460
|
+
});
|
|
638461
|
+
if (resp.status === 404) return `No Ollama weights found for ${model.id}.`;
|
|
638462
|
+
if (!resp.ok) {
|
|
638463
|
+
const text = await resp.text().catch(() => "");
|
|
638464
|
+
throw new Error(`Ollama delete failed for ${model.id}: HTTP ${resp.status}${text ? ` - ${text.slice(0, 300)}` : ""}`);
|
|
638465
|
+
}
|
|
638466
|
+
return `Deleted Ollama weights for ${model.id}.`;
|
|
638467
|
+
}
|
|
638468
|
+
const results = model.dependencies.map((repo) => deleteCachedModel(repo));
|
|
638469
|
+
const freed = results.reduce((sum, item) => sum + item.bytesFreed, 0);
|
|
638470
|
+
const lines = results.map((item) => `- ${item.repo}: ${item.bytesFreed > 0 ? formatModelBytes(item.bytesFreed) : "not cached"}`);
|
|
638471
|
+
return [`Deleted cached model weights for ${model.label}.`, `Freed: ${formatModelBytes(freed)}`, ...lines].join("\n");
|
|
638472
|
+
}
|
|
638473
|
+
telegramCreativeRootForChat(chatId) {
|
|
638474
|
+
return telegramCreativeWorkspaceRoot(this.repoRoot || process.cwd(), chatId);
|
|
638475
|
+
}
|
|
638476
|
+
buildTelegramGenerationTool(root, model) {
|
|
638477
|
+
if (model.generation === "image") {
|
|
638478
|
+
return {
|
|
638479
|
+
tool: new ImageGenerateTool(root, this.agentConfig?.backendUrl, {
|
|
638480
|
+
model: model.id,
|
|
638481
|
+
backend: model.backend
|
|
638482
|
+
}),
|
|
638483
|
+
args: { model: model.id, backend: model.backend }
|
|
638484
|
+
};
|
|
638485
|
+
}
|
|
638486
|
+
if (model.generation === "sound" || model.generation === "music") {
|
|
638487
|
+
return {
|
|
638488
|
+
tool: new AudioGenerateTool(root, model.generation === "music" ? { musicModel: model.id, musicBackend: model.backend } : { soundModel: model.id, soundBackend: model.backend }),
|
|
638489
|
+
args: { kind: model.generation, model: model.id, backend: model.backend }
|
|
638490
|
+
};
|
|
638491
|
+
}
|
|
638492
|
+
if (model.generation === "video") {
|
|
638493
|
+
return {
|
|
638494
|
+
tool: new VideoGenerateTool(root, {
|
|
638495
|
+
model: model.id,
|
|
638496
|
+
backend: model.backend,
|
|
638497
|
+
defaultKind: "t2v"
|
|
638498
|
+
}),
|
|
638499
|
+
args: { model: model.id, backend: model.backend, kind: "t2v" }
|
|
638500
|
+
};
|
|
638501
|
+
}
|
|
638502
|
+
const kind = model.generation === "cad" ? "cad" : "3d";
|
|
638503
|
+
return {
|
|
638504
|
+
tool: new ModelGenerateTool(root, kind === "cad" ? { cadModel: model.id, cadBackend: model.backend } : { model3dModel: model.id, model3dBackend: model.backend }),
|
|
638505
|
+
args: { kind, model: model.id, backend: model.backend }
|
|
638506
|
+
};
|
|
638507
|
+
}
|
|
638508
|
+
attachTelegramGenerationProgress(chatId, messageId, heading, model, tool) {
|
|
638509
|
+
if (!messageId) return;
|
|
638510
|
+
const setProgress = tool.setProgressCallback;
|
|
638511
|
+
if (typeof setProgress !== "function") return;
|
|
638512
|
+
let lastEdit = 0;
|
|
638513
|
+
setProgress.call(tool, (event) => {
|
|
638514
|
+
const now = Date.now();
|
|
638515
|
+
if (now - lastEdit < 2500 && event.percent === void 0) return;
|
|
638516
|
+
lastEdit = now;
|
|
638517
|
+
const percent = typeof event.percent === "number" ? ` ${Math.round(event.percent)}%` : "";
|
|
638518
|
+
const elapsed = typeof event.elapsedMs === "number" ? `
|
|
638519
|
+
Elapsed: ${Math.round(event.elapsedMs / 1e3)}s` : "";
|
|
638520
|
+
const html = [
|
|
638521
|
+
`<b>${escapeTelegramHTML(heading)}</b>`,
|
|
638522
|
+
`Model: <code>${escapeTelegramHTML(model.id)}</code>`,
|
|
638523
|
+
`Stage: <code>${escapeTelegramHTML(String(event.stage || "process"))}</code>${percent}`,
|
|
638524
|
+
escapeTelegramHTML(String(event.message || "")),
|
|
638525
|
+
elapsed
|
|
638526
|
+
].filter(Boolean).join("\n");
|
|
638527
|
+
void this.editLiveMessage(chatId, messageId, html).catch(() => false);
|
|
638528
|
+
});
|
|
638529
|
+
}
|
|
638530
|
+
async runTelegramGenerationPrewarm(chatId, model) {
|
|
638531
|
+
const root = this.telegramCreativeRootForChat(chatId);
|
|
638532
|
+
const { tool, args } = this.buildTelegramGenerationTool(root, model);
|
|
638533
|
+
const liveId = await this.sendMessageHTML(
|
|
638534
|
+
chatId,
|
|
638535
|
+
`<b>Loading ${escapeTelegramHTML(model.label)}</b>
|
|
638536
|
+
Model: <code>${escapeTelegramHTML(model.id)}</code>`
|
|
638537
|
+
);
|
|
638538
|
+
this.attachTelegramGenerationProgress(chatId, liveId, `Loading ${model.label}`, model, tool);
|
|
638539
|
+
const action = model.generation === "model3d" || model.generation === "cad" ? "check" : "prewarm";
|
|
638540
|
+
const result = await tool.execute({ ...args, action });
|
|
638541
|
+
const html = result.success ? `<b>Model load/check complete</b>
|
|
638542
|
+
<blockquote expandable>${escapeTelegramHTML(result.output || "Ready.")}</blockquote>` : `<b>Model load/check failed</b>
|
|
638543
|
+
<blockquote expandable>${escapeTelegramHTML(result.error || result.output || "Unknown failure.")}</blockquote>`;
|
|
638544
|
+
if (liveId) await this.editLiveMessage(chatId, liveId, html).catch(() => false);
|
|
638545
|
+
else await this.sendMessageHTML(chatId, html);
|
|
638546
|
+
}
|
|
638547
|
+
async expandTelegramGenerationPrompt(intent, prompt, modelId = intent.model, backend = intent.backend ?? "auto") {
|
|
638548
|
+
const raw = prompt.trim();
|
|
638549
|
+
if (!raw || !this.agentConfig) return raw;
|
|
638550
|
+
try {
|
|
638551
|
+
const llm = new OllamaAgenticBackend(
|
|
638552
|
+
this.agentConfig.backendUrl,
|
|
638553
|
+
this.agentConfig.model,
|
|
638554
|
+
this.agentConfig.apiKey
|
|
638555
|
+
);
|
|
638556
|
+
const system = [
|
|
638557
|
+
"You rewrite a user's short media-generation request into a stronger production prompt.",
|
|
638558
|
+
"Preserve the user's subject and intent. Add concrete sensory, compositional, timing, material, and style details only where useful.",
|
|
638559
|
+
"Do not add policy commentary, labels, quotes, or explanation. Output only the expanded prompt."
|
|
638560
|
+
].join(" ");
|
|
638561
|
+
const user = [
|
|
638562
|
+
`Generation type: ${intent.generation}`,
|
|
638563
|
+
`Target model: ${modelId}`,
|
|
638564
|
+
`Backend: ${backend}`,
|
|
638565
|
+
"",
|
|
638566
|
+
"User prompt:",
|
|
638567
|
+
raw
|
|
638568
|
+
].join("\n");
|
|
638569
|
+
const result = await this.telegramObservableInference(
|
|
638570
|
+
llm,
|
|
638571
|
+
telegramThinkSuppressedRequest({
|
|
638572
|
+
messages: [
|
|
638573
|
+
{ role: "system", content: system },
|
|
638574
|
+
{ role: "user", content: user }
|
|
638575
|
+
],
|
|
638576
|
+
tools: [],
|
|
638577
|
+
temperature: 0.4,
|
|
638578
|
+
maxTokens: 700,
|
|
638579
|
+
timeoutMs: Math.min(Math.max(this.agentConfig.timeoutMs ?? 3e4, 5e3), 3e4)
|
|
638580
|
+
}),
|
|
638581
|
+
"chat-fast-path",
|
|
638582
|
+
`generation-prompt:${String(intent.chatId)}:${intent.fromUserId}`
|
|
638583
|
+
);
|
|
638584
|
+
const text = result.choices[0]?.message?.content;
|
|
638585
|
+
if (typeof text !== "string") return raw;
|
|
638586
|
+
const cleaned = text.replace(/^["'`]+|["'`]+$/g, "").replace(/^(?:expanded prompt|prompt|output)\s*:\s*/i, "").trim();
|
|
638587
|
+
return cleaned || raw;
|
|
638588
|
+
} catch {
|
|
638589
|
+
return raw;
|
|
638590
|
+
}
|
|
638591
|
+
}
|
|
638592
|
+
installTelegramPromptExpander(tool, intent) {
|
|
638593
|
+
const setExpander = tool.setPromptExpander;
|
|
638594
|
+
if (typeof setExpander !== "function") return;
|
|
638595
|
+
setExpander.call(tool, async (ctx3) => this.expandTelegramGenerationPrompt(intent, ctx3.originalPrompt, ctx3.model, ctx3.backend));
|
|
638596
|
+
}
|
|
638597
|
+
async sendTelegramGenerationArtifacts(msg, root, result) {
|
|
638598
|
+
const paths = /* @__PURE__ */ new Set();
|
|
638599
|
+
for (const path12 of result.mutatedFiles ?? []) {
|
|
638600
|
+
if (typeof path12 === "string" && path12.trim()) paths.add(resolve53(path12));
|
|
638601
|
+
}
|
|
638602
|
+
for (const path12 of collectGeneratedArtifactPathsFromText(result.output || "", root)) {
|
|
638603
|
+
paths.add(resolve53(path12));
|
|
638604
|
+
}
|
|
638605
|
+
let sent = 0;
|
|
638606
|
+
for (const path12 of paths) {
|
|
638607
|
+
if (!existsSync127(path12) || !statSync46(path12).isFile()) continue;
|
|
638608
|
+
const kind = classifyMedia(path12) ?? "document";
|
|
638609
|
+
const messageId = await this.sendMediaReference(msg.chatId, {
|
|
638610
|
+
original: path12,
|
|
638611
|
+
value: path12,
|
|
638612
|
+
kind,
|
|
638613
|
+
source: "file",
|
|
638614
|
+
audioAsVoice: kind === "voice"
|
|
638615
|
+
}, {
|
|
638616
|
+
replyToMessageId: msg.messageId
|
|
638617
|
+
}).catch(() => null);
|
|
638618
|
+
if (messageId !== null) sent++;
|
|
638619
|
+
}
|
|
638620
|
+
return sent;
|
|
638621
|
+
}
|
|
638622
|
+
async executeTelegramGenerationPrompt(msg, intent) {
|
|
638623
|
+
const descriptor = this.telegramModelDescriptor(intent.generation, intent.model) ?? {
|
|
638624
|
+
generation: intent.generation,
|
|
638625
|
+
id: intent.model,
|
|
638626
|
+
label: intent.label,
|
|
638627
|
+
backend: intent.backend || "auto",
|
|
638628
|
+
category: "",
|
|
638629
|
+
sizeClass: "",
|
|
638630
|
+
quality: "",
|
|
638631
|
+
note: "",
|
|
638632
|
+
cachedBytes: 0,
|
|
638633
|
+
dependencies: [intent.model]
|
|
638634
|
+
};
|
|
638635
|
+
const root = this.telegramCreativeRootForChat(msg.chatId);
|
|
638636
|
+
const { tool, args } = this.buildTelegramGenerationTool(root, descriptor);
|
|
638637
|
+
this.installTelegramPromptExpander(tool, intent);
|
|
638638
|
+
const liveId = await this.sendMessageHTML(
|
|
638639
|
+
msg.chatId,
|
|
638640
|
+
[
|
|
638641
|
+
`<b>${escapeTelegramHTML(this.telegramGenerationLabel(intent.generation))} generation</b>`,
|
|
638642
|
+
`Model: <code>${escapeTelegramHTML(intent.model)}</code>`,
|
|
638643
|
+
"Expanding prompt and preparing runtime..."
|
|
638644
|
+
].join("\n"),
|
|
638645
|
+
msg.messageId
|
|
638646
|
+
);
|
|
638647
|
+
this.attachTelegramGenerationProgress(msg.chatId, liveId, `${this.telegramGenerationLabel(intent.generation)} generation`, descriptor, tool);
|
|
638648
|
+
const rawPrompt = msg.text.trim();
|
|
638649
|
+
const prompt = intent.generation === "image" || intent.generation === "video" ? rawPrompt : await this.expandTelegramGenerationPrompt(intent, rawPrompt, descriptor.id, descriptor.backend);
|
|
638650
|
+
const result = await tool.execute({
|
|
638651
|
+
...args,
|
|
638652
|
+
action: "generate",
|
|
638653
|
+
prompt,
|
|
638654
|
+
expand_prompt: true
|
|
638655
|
+
});
|
|
638656
|
+
const artifactCount = result.success ? await this.sendTelegramGenerationArtifacts(msg, root, result) : 0;
|
|
638657
|
+
const summary = result.success ? [
|
|
638658
|
+
`<b>Generation complete</b>`,
|
|
638659
|
+
artifactCount > 0 ? `Sent ${artifactCount} artifact${artifactCount === 1 ? "" : "s"}.` : "No artifact file was detected in the tool result.",
|
|
638660
|
+
`<blockquote expandable>${escapeTelegramHTML(result.output || result.llmContent || "Done.")}</blockquote>`
|
|
638661
|
+
].join("\n") : [
|
|
638662
|
+
`<b>Generation failed</b>`,
|
|
638663
|
+
`<blockquote expandable>${escapeTelegramHTML(result.error || result.output || "Unknown failure.")}</blockquote>`
|
|
638664
|
+
].join("\n");
|
|
638665
|
+
if (liveId) await this.editLiveMessage(msg.chatId, liveId, summary).catch(() => false);
|
|
638666
|
+
else await this.sendMessageHTML(msg.chatId, summary, msg.messageId);
|
|
638667
|
+
}
|
|
638668
|
+
async maybeHandleTelegramGenerationPrompt(msg) {
|
|
638669
|
+
this.pruneTelegramGenerationPromptIntents();
|
|
638670
|
+
const key = this.telegramGenerationIntentKey(msg.chatId, msg.fromUserId);
|
|
638671
|
+
const intent = this.telegramGenerationPromptIntents.get(key);
|
|
638672
|
+
if (!intent) return false;
|
|
638673
|
+
if (msg.text.trim().toLowerCase() === "/cancel") {
|
|
638674
|
+
this.telegramGenerationPromptIntents.delete(key);
|
|
638675
|
+
await this.replyToTelegramMessage(msg, "Pending generation cleared.");
|
|
638676
|
+
return true;
|
|
638677
|
+
}
|
|
638678
|
+
if (msg.text.trim().startsWith("/")) return false;
|
|
638679
|
+
this.telegramGenerationPromptIntents.delete(key);
|
|
638680
|
+
await this.executeTelegramGenerationPrompt(msg, intent);
|
|
638681
|
+
return true;
|
|
638682
|
+
}
|
|
638683
|
+
telegramSlashGenerationKind(name10) {
|
|
638684
|
+
if (name10 === "image") return "image";
|
|
638685
|
+
if (name10 === "sound") return "sound";
|
|
638686
|
+
if (name10 === "music") return "music";
|
|
638687
|
+
if (name10 === "video") return "video";
|
|
638688
|
+
return null;
|
|
638689
|
+
}
|
|
638690
|
+
async replyWithTelegramGenerationSetup(msg, generation) {
|
|
638691
|
+
const root = this.telegramCreativeRootForChat(msg.chatId);
|
|
638692
|
+
const settings = resolveSettings(this.repoRoot || process.cwd());
|
|
638693
|
+
let tool;
|
|
638694
|
+
let args;
|
|
638695
|
+
if (generation === "image") {
|
|
638696
|
+
tool = new ImageGenerateTool(root, this.agentConfig?.backendUrl, this.imageGenerationDefaultsForRepo(this.repoRoot || process.cwd()));
|
|
638697
|
+
args = { action: "setup", model: settings.imageModel, backend: settings.imageBackend };
|
|
638698
|
+
} else if (generation === "sound" || generation === "music") {
|
|
638699
|
+
tool = new AudioGenerateTool(root, this.audioGenerationDefaultsForRepo(this.repoRoot || process.cwd()));
|
|
638700
|
+
args = {
|
|
638701
|
+
action: "setup",
|
|
638702
|
+
kind: generation,
|
|
638703
|
+
model: generation === "music" ? settings.musicModel : settings.soundModel,
|
|
638704
|
+
backend: generation === "music" ? settings.musicBackend : settings.soundBackend
|
|
638705
|
+
};
|
|
638706
|
+
} else if (generation === "video") {
|
|
638707
|
+
tool = new VideoGenerateTool(root, this.videoGenerationDefaultsForRepo(this.repoRoot || process.cwd()));
|
|
638708
|
+
args = { action: "setup", model: settings.videoModel, backend: settings.videoBackend };
|
|
638709
|
+
} else {
|
|
638710
|
+
tool = new ModelGenerateTool(root, this.modelGenerationDefaultsForRepo(this.repoRoot || process.cwd()));
|
|
638711
|
+
args = { action: "list_models", kind: generation === "cad" ? "cad" : "3d" };
|
|
638712
|
+
}
|
|
638713
|
+
const result = await tool.execute(args);
|
|
638714
|
+
const html = result.success ? `<b>${escapeTelegramHTML(this.telegramGenerationLabel(generation))} setup</b>
|
|
638715
|
+
<blockquote expandable>${escapeTelegramHTML(result.output || "No setup output.")}</blockquote>` : `<b>${escapeTelegramHTML(this.telegramGenerationLabel(generation))} setup failed</b>
|
|
638716
|
+
<blockquote expandable>${escapeTelegramHTML(result.error || result.output || "Unknown failure.")}</blockquote>`;
|
|
638717
|
+
await this.replyToTelegramMessage(msg, html, { html: true });
|
|
638718
|
+
}
|
|
638719
|
+
async handleTelegramGenerationSlashCommand(msg, isAdmin, normalizedCommandText) {
|
|
638720
|
+
const trimmed = normalizedCommandText.trim();
|
|
638721
|
+
if (!trimmed.startsWith("/")) return false;
|
|
638722
|
+
const [rawName = "", rawSub = ""] = trimmed.split(/\s+/);
|
|
638723
|
+
const name10 = rawName.slice(1).split("@")[0]?.toLowerCase() ?? "";
|
|
638724
|
+
const sub = rawSub.toLowerCase();
|
|
638725
|
+
if (name10 === "models") {
|
|
638726
|
+
if (!sub) {
|
|
638727
|
+
await this.replyWithTelegramCommandMenu(msg, isAdmin, "generative", "models");
|
|
638728
|
+
return true;
|
|
638729
|
+
}
|
|
638730
|
+
if (sub === "3d" || sub === "cad") {
|
|
638731
|
+
await this.replyWithTelegramModelBrowser(msg, isAdmin, sub === "cad" ? "cad" : "model3d");
|
|
638732
|
+
return true;
|
|
638733
|
+
}
|
|
638734
|
+
return false;
|
|
638735
|
+
}
|
|
638736
|
+
const generation = this.telegramSlashGenerationKind(name10);
|
|
638737
|
+
if (!generation) return false;
|
|
638738
|
+
if (!sub) {
|
|
638739
|
+
await this.replyWithTelegramCommandMenu(msg, isAdmin, "generative", name10);
|
|
638740
|
+
return true;
|
|
638741
|
+
}
|
|
638742
|
+
if (sub === "list" || sub === "models" || sub === "menu") {
|
|
638743
|
+
await this.replyWithTelegramModelBrowser(msg, isAdmin, generation);
|
|
638744
|
+
return true;
|
|
638745
|
+
}
|
|
638746
|
+
if (sub === "setup") {
|
|
638747
|
+
if (!isAdmin) {
|
|
638748
|
+
await this.replyToTelegramMessage(msg, "Generation setup requires Telegram admin authentication.");
|
|
638749
|
+
return true;
|
|
638750
|
+
}
|
|
638751
|
+
await this.replyWithTelegramGenerationSetup(msg, generation);
|
|
638752
|
+
return true;
|
|
638753
|
+
}
|
|
638754
|
+
return false;
|
|
638755
|
+
}
|
|
637828
638756
|
collectSessionMetricsSnapshot() {
|
|
637829
638757
|
if (this._metricsProvider) {
|
|
637830
638758
|
try {
|
|
@@ -643417,6 +644345,9 @@ ${summary}` : ""
|
|
|
643417
644345
|
return;
|
|
643418
644346
|
}
|
|
643419
644347
|
const isAdmin = this.isAdminUser(msg);
|
|
644348
|
+
if (await this.maybeHandleTelegramGenerationPrompt(msg)) {
|
|
644349
|
+
return;
|
|
644350
|
+
}
|
|
643420
644351
|
if (msg.text.trim().startsWith("/") && this.isTelegramCommandsMenuCommand(normalizedCommandText)) {
|
|
643421
644352
|
await this.replyWithTelegramCommandMenu(msg, isAdmin, "commands");
|
|
643422
644353
|
return;
|
|
@@ -643430,6 +644361,9 @@ ${summary}` : ""
|
|
|
643430
644361
|
return;
|
|
643431
644362
|
}
|
|
643432
644363
|
const telegramSlash = this.telegramSlashName(normalizedCommandText);
|
|
644364
|
+
if (msg.text.trim().startsWith("/") && await this.handleTelegramGenerationSlashCommand(msg, isAdmin, normalizedCommandText)) {
|
|
644365
|
+
return;
|
|
644366
|
+
}
|
|
643433
644367
|
if (msg.text.trim().startsWith("/") && TELEGRAM_REFLECTION_SLASH_COMMANDS.has(telegramSlash)) {
|
|
643434
644368
|
await this.handleTelegramReflectionSlash(msg, normalizedCommandText);
|
|
643435
644369
|
return;
|
|
@@ -646293,6 +647227,15 @@ Scoped workspace: ${scopedRoot}`,
|
|
|
646293
647227
|
});
|
|
646294
647228
|
return;
|
|
646295
647229
|
}
|
|
647230
|
+
if (result.item?.action && result.item.action.type !== "command") {
|
|
647231
|
+
const actionResult = await this.handleTelegramCommandMenuAction(callback, menuState, result.item.action);
|
|
647232
|
+
if (actionResult.handled) {
|
|
647233
|
+
if (actionResult.answered) answered = true;
|
|
647234
|
+
if (actionResult.answerText) answerText2 = actionResult.answerText;
|
|
647235
|
+
if (actionResult.alert) alert2 = true;
|
|
647236
|
+
return;
|
|
647237
|
+
}
|
|
647238
|
+
}
|
|
646296
647239
|
if (result.command) {
|
|
646297
647240
|
if (!this.commandHandler) {
|
|
646298
647241
|
answerText2 = "No command handler is available.";
|
|
@@ -667079,7 +668022,7 @@ function handleHelp(req2, res) {
|
|
|
667079
668022
|
override: "Operators can override per-tool classification via OMNIUS_TOOL_OVERRIDES env var (JSON map of name → partial security info).",
|
|
667080
668023
|
filters: "GET /v1/tools supports ?category=, ?scope=, ?risk=, ?off_device=true|false (Q9). E.g. /v1/tools?scope=read&off_device=true returns the safe-to-expose set.",
|
|
667081
668024
|
profiles: "Tool profiles are enforced before tool exposure and again at execution. Resolution order: preset, working_directory/.omnius/profiles, ~/.omnius/profiles. Missing named profiles fail closed.",
|
|
667082
|
-
bookkeeping: "For deterministic tracking, direct-call todo_write, todo_read, working_notes, and task_complete with profile:'
|
|
668025
|
+
bookkeeping: "For deterministic tracking, direct-call todo_write, todo_read, working_notes, and task_complete with profile:'bookkeeping-tracking' and a stable session_id. Do not use /v1/run for bookkeeping-only mutations."
|
|
667083
668026
|
},
|
|
667084
668027
|
runtime_keys: {
|
|
667085
668028
|
"GET /v1/keys": "List runtime keys (admin scope). Secrets masked.",
|