Add jev.probabilities and an eight-way aspect ratio default Jev script

This commit is contained in:
kantan-kanto
2026-09-26 18:17:30 +09:00
parent c65a9a8f4a
commit 40b2a5fa92
3 changed files with 36 additions and 7 deletions
+6
View File
@@ -92,6 +92,7 @@ Functions (`state` is a string, dict, or list; text only):
- `jev.yes(state, question[, threshold])` → `bool` (yes-probability ≥ `threshold`, default `0.5`)
- `jev.noul(state, question)` → yes-probability (`0.0`–`1.0`). Compare it with a threshold; using it directly as a condition raises an error.
- `jev.choice(state, question, options)` → the most likely option (`str`). `options` is a list of 2–16 labels, or a dict of labels to descriptions.
- `jev.probabilities(state, question, options)` → `dict` of each option to its probability (sums to `1.0`). Same `options` as `jev.choice`; asking both with the same arguments runs the model once.
- `jev.score(state, question, levels)` → expected level (`float`, `0` to `len(levels) - 1`) for 2–16 ordered level descriptions
```python
@@ -103,8 +104,13 @@ else:
kind = jev.choice(it1, "Which orientation suits this prompt?", ["portrait", "landscape"])
ov2, ov3 = (384, 512) if kind == "portrait" else (512, 384)
probs = jev.probabilities(it1, "Which orientation suits this prompt?", ["portrait", "landscape"])
ot1 = f"{kind} (portrait: {probs['portrait']:.3f}, landscape: {probs['landscape']:.3f})"
```
The node's default script picks one of eight aspect ratios for the prompt in `it1`, outputs its width and height to `out_value_1` / `out_value_2`, and lists every ratio's probability in `out_text_1`.
Notes:
- Each question takes one forward pass (about 1–1.5 s with a 4B–9B model on GPU); the first question also loads the model.
- Probabilities are uncalibrated and depend on the model; test thresholds on your own inputs.
+25 -4
View File
@@ -517,6 +517,10 @@ class _SafeScriptInterpreter:
if len(args) != 3:
raise ValueError("jev.choice() expects 3 arguments")
return self.jev.choice(*args)
if name == "probabilities":
if len(args) != 3:
raise ValueError("jev.probabilities() expects 3 arguments")
return self.jev.probabilities(*args)
if name == "score":
if len(args) != 3:
raise ValueError("jev.score() expects 3 arguments")
@@ -868,11 +872,28 @@ class MultiOutputScriptJev(MultiOutputScript):
"# Jev: jev.yes(state, question[, threshold]) -> bool\n"
"# jev.noul(state, question) -> probability\n"
"# jev.choice(state, question, options) -> str\n"
"# jev.probabilities(state, question, options) -> {option: probability}\n"
"# jev.score(state, question, levels) -> float\n"
"# Example:\n"
"kind = jev.choice(it1, \"What orientation suits this prompt?\", [\"portrait\", \"landscape\"])\n"
"ov1, ov2 = (384, 512) if kind == \"portrait\" else (512, 384)\n"
"ot1 = kind\n"
"# Example: pick an aspect ratio for the prompt in it1\n"
"sizes = {\n"
" \"1:1 (Square)\": (1024, 1024),\n"
" \"2:3 (Portrait Photo)\": (832, 1216),\n"
" \"3:2 (Photo)\": (1216, 832),\n"
" \"3:4 (Portrait Standard)\": (896, 1152),\n"
" \"4:3 (Standard)\": (1152, 896),\n"
" \"9:16 (Portrait Widescreen)\": (768, 1344),\n"
" \"16:9 (Widescreen)\": (1344, 768),\n"
" \"21:9 (Ultrawide)\": (1536, 640),\n"
"}\n"
"question = \"Which image aspect ratio best suits this prompt?\"\n"
"ratios = list(sizes)\n"
"best = jev.choice(it1, question, ratios)\n"
"probs = jev.probabilities(it1, question, ratios)\n"
"ov1, ov2 = sizes[best]\n"
"lines = [best]\n"
"for ratio in ratios:\n"
" lines.append(f\"{ratio}: {probs[ratio]:.3f}\")\n"
"ot1 = \"\\n\".join(lines)\n"
)
types["required"]["model"] = (list(list_local_models()),)
types["required"]["max_jev_calls"] = ("INT", {"default": 8, "min": 1, "max": 64})
+5 -3
View File
@@ -49,12 +49,14 @@ class JevBackend:
return self._ask(state, question, ["Yes", "No"])[0]
def choice(self, state: Any, question: Any, options: list | dict) -> str:
probabilities = self.probabilities(state, question, options)
return max(probabilities, key=probabilities.get)
def probabilities(self, state: Any, question: Any, options: list | dict) -> dict[str, float]:
if isinstance(options, list):
options = {str(option): None for option in options}
labels = list(options)
descriptions = [label if description is None else f"{label}: {description}" for label, description in options.items()]
probabilities = self._ask(state, question, descriptions)
return labels[probabilities.index(max(probabilities))]
return dict(zip(options, self._ask(state, question, descriptions)))
def score(self, state: Any, question: Any, levels: list) -> float:
probabilities = self._ask(state, question, list(levels))