Restore MSR character analyze config and parsing
This commit is contained in:
@@ -92,6 +92,9 @@ function setWidgetValue(node, widget, value) {
|
|||||||
function syncFormFromWidgets(node) {
|
function syncFormFromWidgets(node) {
|
||||||
const description = findWidget(node, "description")?.value || "";
|
const description = findWidget(node, "description")?.value || "";
|
||||||
const alias = findWidget(node, "alias")?.value || "";
|
const alias = findWidget(node, "alias")?.value || "";
|
||||||
|
const provider = findWidget(node, "analyze_provider")?.value || "ollama";
|
||||||
|
const model = findWidget(node, "analyze_model")?.value || "";
|
||||||
|
const baseUrl = findWidget(node, "analyze_base_url")?.value || "";
|
||||||
|
|
||||||
if (node._msrAliasInput && node._msrAliasInput.value !== alias) {
|
if (node._msrAliasInput && node._msrAliasInput.value !== alias) {
|
||||||
node._msrAliasInput.value = alias;
|
node._msrAliasInput.value = alias;
|
||||||
@@ -104,6 +107,7 @@ function syncFormFromWidgets(node) {
|
|||||||
alias
|
alias
|
||||||
? `Alias: <span style="color:#e8e8e8">@${String(alias).replace(/^@/, "")}</span>`
|
? `Alias: <span style="color:#e8e8e8">@${String(alias).replace(/^@/, "")}</span>`
|
||||||
: "Alias: <span style=\"color:#666\">none</span>",
|
: "Alias: <span style=\"color:#666\">none</span>",
|
||||||
|
`<div style="margin-top:4px;color:#888">Analyze: ${provider}${model ? ` / ${model}` : ""}${baseUrl ? ` / ${baseUrl}` : ""}</div>`,
|
||||||
description
|
description
|
||||||
? `<div style="margin-top:4px;color:#d0d0d0">${description}</div>`
|
? `<div style="margin-top:4px;color:#d0d0d0">${description}</div>`
|
||||||
: `<div style="margin-top:4px;color:#666">Description will be used for prompt replacement outside MSR prefix mode.</div>`,
|
: `<div style="margin-top:4px;color:#666">Description will be used for prompt replacement outside MSR prefix mode.</div>`,
|
||||||
@@ -189,6 +193,36 @@ function buildCharacterUi(node) {
|
|||||||
gap: "6px",
|
gap: "6px",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const settingsButton = document.createElement("button");
|
||||||
|
settingsButton.type = "button";
|
||||||
|
settingsButton.textContent = "Analyze Settings";
|
||||||
|
Object.assign(settingsButton.style, {
|
||||||
|
alignSelf: "flex-start",
|
||||||
|
background: "#252525",
|
||||||
|
color: "#ddd",
|
||||||
|
border: "1px solid #444",
|
||||||
|
borderRadius: "6px",
|
||||||
|
padding: "6px 10px",
|
||||||
|
fontSize: "11px",
|
||||||
|
cursor: "pointer",
|
||||||
|
});
|
||||||
|
settingsButton.addEventListener("click", () => {
|
||||||
|
const providerWidget = findWidget(node, "analyze_provider");
|
||||||
|
const baseUrlWidget = findWidget(node, "analyze_base_url");
|
||||||
|
const modelWidget = findWidget(node, "analyze_model");
|
||||||
|
const provider = window.prompt("Analyze provider: ollama, lmstudio, custom, off", providerWidget?.value || "ollama");
|
||||||
|
if (provider == null) return;
|
||||||
|
const baseUrl = window.prompt("Analyze base URL (blank = default)", baseUrlWidget?.value || "");
|
||||||
|
if (baseUrl == null) return;
|
||||||
|
const model = window.prompt("Analyze model (blank = provider default)", modelWidget?.value || "");
|
||||||
|
if (model == null) return;
|
||||||
|
setWidgetValue(node, providerWidget, provider.trim() || "ollama");
|
||||||
|
setWidgetValue(node, baseUrlWidget, baseUrl.trim());
|
||||||
|
setWidgetValue(node, modelWidget, model.trim());
|
||||||
|
syncFormFromWidgets(node);
|
||||||
|
node.setDirtyCanvas?.(true, true);
|
||||||
|
});
|
||||||
|
|
||||||
const aliasLabel = document.createElement("label");
|
const aliasLabel = document.createElement("label");
|
||||||
aliasLabel.textContent = "Alias";
|
aliasLabel.textContent = "Alias";
|
||||||
Object.assign(aliasLabel.style, {
|
Object.assign(aliasLabel.style, {
|
||||||
@@ -245,6 +279,7 @@ function buildCharacterUi(node) {
|
|||||||
form.appendChild(aliasInput);
|
form.appendChild(aliasInput);
|
||||||
form.appendChild(descLabel);
|
form.appendChild(descLabel);
|
||||||
form.appendChild(descInput);
|
form.appendChild(descInput);
|
||||||
|
form.appendChild(settingsButton);
|
||||||
|
|
||||||
container.appendChild(previewRow);
|
container.appendChild(previewRow);
|
||||||
container.appendChild(meta);
|
container.appendChild(meta);
|
||||||
@@ -261,6 +296,9 @@ function buildCharacterUi(node) {
|
|||||||
async function analyzeCharacterNode(node, buttonWidget) {
|
async function analyzeCharacterNode(node, buttonWidget) {
|
||||||
const descriptionWidget = findWidget(node, "description");
|
const descriptionWidget = findWidget(node, "description");
|
||||||
if (!descriptionWidget) return;
|
if (!descriptionWidget) return;
|
||||||
|
const providerWidget = findWidget(node, "analyze_provider");
|
||||||
|
const baseUrlWidget = findWidget(node, "analyze_base_url");
|
||||||
|
const modelWidget = findWidget(node, "analyze_model");
|
||||||
|
|
||||||
buttonWidget.label = "Analyzing...";
|
buttonWidget.label = "Analyzing...";
|
||||||
node.setDirtyCanvas?.(true, true);
|
node.setDirtyCanvas?.(true, true);
|
||||||
@@ -280,7 +318,9 @@ async function analyzeCharacterNode(node, buttonWidget) {
|
|||||||
const response = await api.fetchApi("/ltx_director/analyze_character", {
|
const response = await api.fetchApi("/ltx_director/analyze_character", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
provider: "ollama",
|
provider: providerWidget?.value || "ollama",
|
||||||
|
base_url: baseUrlWidget?.value || "",
|
||||||
|
model: modelWidget?.value || "",
|
||||||
image_b64: images,
|
image_b64: images,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
@@ -332,6 +372,18 @@ app.registerExtension({
|
|||||||
buttonWidget.label = "Analyze with Ollama";
|
buttonWidget.label = "Analyze with Ollama";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!findWidget(this, "analyze_provider")) {
|
||||||
|
this.addWidget("combo", "analyze_provider", "ollama", null, {
|
||||||
|
values: ["ollama", "lmstudio", "custom", "off"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!findWidget(this, "analyze_base_url")) {
|
||||||
|
this.addWidget("text", "analyze_base_url", "");
|
||||||
|
}
|
||||||
|
if (!findWidget(this, "analyze_model")) {
|
||||||
|
this.addWidget("text", "analyze_model", "");
|
||||||
|
}
|
||||||
|
|
||||||
if (!this._msrPreviewWidget) {
|
if (!this._msrPreviewWidget) {
|
||||||
const previewContainer = buildCharacterUi(this);
|
const previewContainer = buildCharacterUi(this);
|
||||||
this._msrPreviewWidget = this.addDOMWidget("msr_character_ui", "msr_character_ui", previewContainer, {
|
this._msrPreviewWidget = this.addDOMWidget("msr_character_ui", "msr_character_ui", previewContainer, {
|
||||||
@@ -358,7 +410,7 @@ app.registerExtension({
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
["alias", "description"].forEach((widgetName) => {
|
["alias", "description", "analyze_provider", "analyze_base_url", "analyze_model"].forEach((widgetName) => {
|
||||||
hideNodeWidget(findWidget(this, widgetName));
|
hideNodeWidget(findWidget(this, widgetName));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -445,6 +445,27 @@ _ANALYZE_PROMPT = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_ollama_generated_text(resp_json: dict) -> str:
|
||||||
|
if not isinstance(resp_json, dict):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
candidates = [resp_json.get("response"), resp_json.get("content"), resp_json.get("thinking")]
|
||||||
|
message = resp_json.get("message")
|
||||||
|
if isinstance(message, dict):
|
||||||
|
candidates.extend([
|
||||||
|
message.get("content"),
|
||||||
|
message.get("reasoning_content"),
|
||||||
|
message.get("thinking"),
|
||||||
|
])
|
||||||
|
|
||||||
|
for candidate in candidates:
|
||||||
|
if isinstance(candidate, str):
|
||||||
|
text = candidate.strip()
|
||||||
|
if text:
|
||||||
|
return text
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def _compress_analysis_image_b64(b64_payload: str, max_dim: int = 768, quality: int = 82) -> str:
|
def _compress_analysis_image_b64(b64_payload: str, max_dim: int = 768, quality: int = 82) -> str:
|
||||||
"""Shrink analysis images so multimodal providers do not burn their full context on pixels."""
|
"""Shrink analysis images so multimodal providers do not burn their full context on pixels."""
|
||||||
try:
|
try:
|
||||||
@@ -531,12 +552,12 @@ async def analyze_character_endpoint(request):
|
|||||||
retry_err = await retry_response.text()
|
retry_err = await retry_response.text()
|
||||||
return web.json_response({"status": "error", "message": f"Ollama HTTP {retry_response.status}: {retry_err}"})
|
return web.json_response({"status": "error", "message": f"Ollama HTTP {retry_response.status}: {retry_err}"})
|
||||||
resp_json = await retry_response.json()
|
resp_json = await retry_response.json()
|
||||||
generated_text = (resp_json.get("response") or "").strip()
|
generated_text = _extract_ollama_generated_text(resp_json)
|
||||||
else:
|
else:
|
||||||
return web.json_response({"status": "error", "message": f"Ollama HTTP {response.status}: {err_txt}"})
|
return web.json_response({"status": "error", "message": f"Ollama HTTP {response.status}: {err_txt}"})
|
||||||
else:
|
else:
|
||||||
resp_json = await response.json()
|
resp_json = await response.json()
|
||||||
generated_text = (resp_json.get("response") or "").strip()
|
generated_text = _extract_ollama_generated_text(resp_json)
|
||||||
else:
|
else:
|
||||||
# OpenAI-compatible vision chat (LM Studio / Custom).
|
# OpenAI-compatible vision chat (LM Studio / Custom).
|
||||||
content = [{"type": "text", "text": _ANALYZE_PROMPT}]
|
content = [{"type": "text", "text": _ANALYZE_PROMPT}]
|
||||||
|
|||||||
Reference in New Issue
Block a user