Fix Krea2 atlas cue prompt formatting
This commit is contained in:
@@ -6843,6 +6843,11 @@ def smoke_krea2_pov_atlas_variant_prompt_routes() -> None:
|
||||
for cue in variant.get("prompt_cues") or []:
|
||||
cue_text = _expect_text(f"{key}.prompt_cue", cue, 8).lower()
|
||||
_expect(cue_text in prompt, f"{key} final Krea prompt lost atlas cue {cue_text!r}: {prompt}")
|
||||
atlas_action_prompt = prompt.split(" camera is ", 1)[0]
|
||||
_expect(
|
||||
"; " not in atlas_action_prompt,
|
||||
f"{key} final Krea prompt kept semicolon-delimited atlas cue formatting: {prompt}",
|
||||
)
|
||||
_expect(
|
||||
"framed as " not in prompt and "the image is framed as " not in prompt,
|
||||
f"{key} final Krea prompt kept generic composition text after atlas route: {prompt}",
|
||||
@@ -6871,10 +6876,12 @@ def smoke_krea2_pov_atlas_variant_prompt_routes() -> None:
|
||||
):
|
||||
_expect(forbidden not in prompt, f"{key} final Krea prompt kept generic detail fragment {forbidden!r}: {prompt}")
|
||||
for required in (
|
||||
"the woman kneels directly below the viewer between his feet",
|
||||
"mouth seals around the centered shaft",
|
||||
"one hand wraps the base",
|
||||
):
|
||||
_expect(required in prompt, f"{key} final Krea prompt lost compact contact cue {required!r}: {prompt}")
|
||||
_expect("one woman" not in prompt, f"{key} final Krea prompt split the subject with 'one woman': {prompt}")
|
||||
for avoid in variant.get("avoid_cues") or []:
|
||||
avoid_text = _expect_text(f"{key}.avoid_cue", avoid, 4).lower()
|
||||
_expect(avoid_text not in prompt, f"{key} final Krea prompt leaked avoid cue {avoid_text!r}: {prompt}")
|
||||
@@ -10394,6 +10401,106 @@ def smoke_sxcp_mcp_client_cli_policy() -> None:
|
||||
_expect("--arguments-json" in call_help.stdout, "sxcp MCP client help lost JSON argument option")
|
||||
|
||||
|
||||
def smoke_watch_prompt_image_folder_cli_policy() -> None:
|
||||
helper_path = ROOT / "tools" / "watch_prompt_image_folder.sh"
|
||||
_expect(helper_path.exists(), "watch prompt/image folder helper is missing")
|
||||
_expect(helper_path.stat().st_mode & 0o111, "watch prompt/image folder helper must be executable")
|
||||
|
||||
help_result = subprocess.run(
|
||||
[str(helper_path), "--help"],
|
||||
cwd=ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
_expect(help_result.returncode == 0, f"watch helper --help failed: {help_result.stderr}")
|
||||
for term in ("--folder", "--target", "--notes", "--once", "--dry-run", "tmux send-keys"):
|
||||
_expect(term in help_result.stdout, f"watch helper help lost {term!r}")
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
folder = Path(temp_dir)
|
||||
prompt_path = folder / "atlas_case_001.txt"
|
||||
image_path = folder / "atlas_case_001.png"
|
||||
state_path = folder / "seen.state"
|
||||
notes_path = folder / "prompt-learning.md"
|
||||
prompt_path.write_text("nadir-angle standing male POV test prompt\n", encoding="utf-8")
|
||||
image_path.write_bytes(b"fake-png")
|
||||
|
||||
first_scan = subprocess.run(
|
||||
[
|
||||
str(helper_path),
|
||||
"--folder",
|
||||
str(folder),
|
||||
"--target",
|
||||
"codex:1.0",
|
||||
"--notes",
|
||||
str(notes_path),
|
||||
"--state",
|
||||
str(state_path),
|
||||
"--once",
|
||||
"--dry-run",
|
||||
],
|
||||
cwd=ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
_expect(first_scan.returncode == 0, f"watch helper dry-run scan failed: {first_scan.stderr}")
|
||||
_expect("tmux send-keys -t codex:1.0" in first_scan.stdout, "watch helper dry-run did not render tmux target")
|
||||
_expect(str(prompt_path) in first_scan.stdout, "watch helper notification lost prompt path")
|
||||
_expect(str(image_path) in first_scan.stdout, "watch helper notification lost image path")
|
||||
_expect(str(notes_path) in first_scan.stdout, "watch helper notification lost notes path")
|
||||
_expect("atlas_case_001.txt" in state_path.read_text(encoding="utf-8"), "watch helper did not record notified prompt")
|
||||
|
||||
second_scan = subprocess.run(
|
||||
[
|
||||
str(helper_path),
|
||||
"--folder",
|
||||
str(folder),
|
||||
"--target",
|
||||
"codex:1.0",
|
||||
"--notes",
|
||||
str(notes_path),
|
||||
"--state",
|
||||
str(state_path),
|
||||
"--once",
|
||||
"--dry-run",
|
||||
],
|
||||
cwd=ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
_expect(second_scan.returncode == 0, f"watch helper repeated dry-run scan failed: {second_scan.stderr}")
|
||||
_expect("no new prompt/image pairs" in second_scan.stdout, "watch helper should dedupe already-notified pairs")
|
||||
|
||||
jpeg_prompt = folder / "atlas_case_002.prompt"
|
||||
jpeg_image = folder / "atlas_case_002.jpg"
|
||||
jpeg_prompt.write_text("side-profile oral prompt\n", encoding="utf-8")
|
||||
jpeg_image.write_bytes(b"fake-jpg")
|
||||
third_scan = subprocess.run(
|
||||
[
|
||||
str(helper_path),
|
||||
"--folder",
|
||||
str(folder),
|
||||
"--target",
|
||||
"codex:1.0",
|
||||
"--notes",
|
||||
str(notes_path),
|
||||
"--state",
|
||||
str(state_path),
|
||||
"--once",
|
||||
"--dry-run",
|
||||
],
|
||||
cwd=ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
_expect(third_scan.returncode == 0, f"watch helper jpeg dry-run scan failed: {third_scan.stderr}")
|
||||
_expect(str(jpeg_prompt) in third_scan.stdout and str(jpeg_image) in third_scan.stdout, "watch helper lost prompt/JPEG pairing")
|
||||
|
||||
|
||||
def smoke_sxcp_prompt_batch_cli_policy() -> None:
|
||||
helper_path = ROOT / "tools" / "sxcp_prompt_batch.py"
|
||||
eval_loop_doc = (ROOT / "docs" / "sxcp-eval-loop.md").read_text(encoding="utf-8")
|
||||
@@ -12180,6 +12287,7 @@ SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [
|
||||
("seed_config_policy", smoke_seed_config_policy),
|
||||
("prompt_route_simulation_policy", smoke_prompt_route_simulation_policy),
|
||||
("sxcp_mcp_client_cli_policy", smoke_sxcp_mcp_client_cli_policy),
|
||||
("watch_prompt_image_folder_cli_policy", smoke_watch_prompt_image_folder_cli_policy),
|
||||
("sxcp_prompt_batch_cli_policy", smoke_sxcp_prompt_batch_cli_policy),
|
||||
("node_camera_registration", smoke_node_camera_registration),
|
||||
("node_route_config_registration", smoke_node_route_config_registration),
|
||||
|
||||
Executable
+248
@@ -0,0 +1,248 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
tools/watch_prompt_image_folder.sh --folder DIR --target TMUX_TARGET [options]
|
||||
|
||||
Watch a folder for prompt/image pairs and notify a selected Byobu/tmux Codex
|
||||
pane using tmux send-keys. Prompt and image files are paired by basename:
|
||||
atlas_case_001.txt + atlas_case_001.png
|
||||
atlas_case_002.prompt + atlas_case_002.jpg
|
||||
|
||||
Required:
|
||||
--folder DIR Folder to watch.
|
||||
--target TARGET tmux target pane, for example session:1.0.
|
||||
|
||||
Options:
|
||||
--notes FILE Notes/output file to mention in the Codex message.
|
||||
Default: DIR/prompt-learning.md
|
||||
--state FILE Seen-state file. Default: DIR/.sxcp_watch_seen
|
||||
--once Scan once and exit.
|
||||
--dry-run Print tmux send-keys commands instead of sending them.
|
||||
--poll-interval SEC Poll/fallback interval in seconds. Default: 2
|
||||
--stable-delay SEC Seconds to wait before accepting a new image. Default: 1
|
||||
--prompt-exts CSV Prompt extensions. Default: txt,prompt
|
||||
--image-exts CSV Image extensions. Default: png,jpg,jpeg,webp
|
||||
-h, --help Show this help.
|
||||
|
||||
Inside Byobu/tmux, get the current target with:
|
||||
tmux display-message -p '#S:#I.#P'
|
||||
EOF
|
||||
}
|
||||
|
||||
folder=""
|
||||
target=""
|
||||
notes=""
|
||||
state=""
|
||||
once=0
|
||||
dry_run=0
|
||||
poll_interval=2
|
||||
stable_delay=1
|
||||
prompt_exts_csv="txt,prompt"
|
||||
image_exts_csv="png,jpg,jpeg,webp"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--folder)
|
||||
folder="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--target)
|
||||
target="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--notes)
|
||||
notes="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--state)
|
||||
state="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--once)
|
||||
once=1
|
||||
shift
|
||||
;;
|
||||
--dry-run)
|
||||
dry_run=1
|
||||
shift
|
||||
;;
|
||||
--poll-interval)
|
||||
poll_interval="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--stable-delay)
|
||||
stable_delay="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--prompt-exts)
|
||||
prompt_exts_csv="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--image-exts)
|
||||
image_exts_csv="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "unknown argument: $1" >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$folder" || -z "$target" ]]; then
|
||||
echo "--folder and --target are required" >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -d "$folder" ]]; then
|
||||
echo "folder does not exist: $folder" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
folder="$(cd "$folder" && pwd -P)"
|
||||
if [[ -z "$notes" ]]; then
|
||||
notes="$folder/prompt-learning.md"
|
||||
fi
|
||||
if [[ -z "$state" ]]; then
|
||||
state="$folder/.sxcp_watch_seen"
|
||||
fi
|
||||
if [[ "$notes" != /* ]]; then
|
||||
notes="$PWD/$notes"
|
||||
fi
|
||||
if [[ "$state" != /* ]]; then
|
||||
state="$PWD/$state"
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$state")"
|
||||
touch "$state"
|
||||
|
||||
IFS=',' read -r -a prompt_exts <<< "$prompt_exts_csv"
|
||||
IFS=',' read -r -a image_exts <<< "$image_exts_csv"
|
||||
|
||||
shell_quote() {
|
||||
printf '%q' "$1"
|
||||
}
|
||||
|
||||
state_key() {
|
||||
local prompt_path="$1"
|
||||
local image_path="$2"
|
||||
printf '%s | %s\n' "$prompt_path" "$image_path"
|
||||
}
|
||||
|
||||
is_seen() {
|
||||
local key="$1"
|
||||
grep -Fxq -- "$key" "$state"
|
||||
}
|
||||
|
||||
mark_seen() {
|
||||
local key="$1"
|
||||
printf '%s\n' "$key" >> "$state"
|
||||
}
|
||||
|
||||
file_size() {
|
||||
wc -c < "$1" | tr -d '[:space:]'
|
||||
}
|
||||
|
||||
wait_for_stable_image() {
|
||||
local image_path="$1"
|
||||
if [[ "$dry_run" -eq 1 || "$stable_delay" == "0" ]]; then
|
||||
return 0
|
||||
fi
|
||||
local before
|
||||
local after
|
||||
before="$(file_size "$image_path")"
|
||||
sleep "$stable_delay"
|
||||
after="$(file_size "$image_path")"
|
||||
[[ "$before" == "$after" ]]
|
||||
}
|
||||
|
||||
find_image_for_prompt() {
|
||||
local prompt_path="$1"
|
||||
local filename
|
||||
local stem
|
||||
local ext
|
||||
local candidate
|
||||
filename="$(basename "$prompt_path")"
|
||||
stem="${filename%.*}"
|
||||
for ext in "${image_exts[@]}"; do
|
||||
candidate="$folder/$stem.$ext"
|
||||
if [[ -f "$candidate" ]]; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
candidate="$folder/$stem.${ext^^}"
|
||||
if [[ -f "$candidate" ]]; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
notify_codex() {
|
||||
local prompt_path="$1"
|
||||
local image_path="$2"
|
||||
local message
|
||||
message="New atlas sample ready: prompt=$prompt_path image=$image_path. Analyze it and append prompt-learning notes to $notes."
|
||||
if [[ "$dry_run" -eq 1 ]]; then
|
||||
printf 'tmux send-keys -t %s %s Enter\n' "$(shell_quote "$target")" "$(shell_quote "$message")"
|
||||
return 0
|
||||
fi
|
||||
tmux send-keys -t "$target" "$message" Enter
|
||||
}
|
||||
|
||||
scan_once() {
|
||||
local notified=0
|
||||
local ext
|
||||
local prompt_path
|
||||
local image_path
|
||||
local key
|
||||
for ext in "${prompt_exts[@]}"; do
|
||||
while IFS= read -r -d '' prompt_path; do
|
||||
if ! image_path="$(find_image_for_prompt "$prompt_path")"; then
|
||||
continue
|
||||
fi
|
||||
key="$(state_key "$prompt_path" "$image_path")"
|
||||
if is_seen "$key"; then
|
||||
continue
|
||||
fi
|
||||
if ! wait_for_stable_image "$image_path"; then
|
||||
continue
|
||||
fi
|
||||
notify_codex "$prompt_path" "$image_path"
|
||||
mark_seen "$key"
|
||||
notified=$((notified + 1))
|
||||
done < <(find "$folder" -maxdepth 1 -type f \( -iname "*.$ext" \) -print0)
|
||||
done
|
||||
if [[ "$notified" -eq 0 && "$once" -eq 1 ]]; then
|
||||
echo "no new prompt/image pairs in $folder"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
if [[ "$once" -eq 1 ]]; then
|
||||
scan_once
|
||||
exit 0
|
||||
fi
|
||||
|
||||
scan_once
|
||||
if command -v inotifywait >/dev/null 2>&1; then
|
||||
while inotifywait -qq -e close_write,create,moved_to "$folder"; do
|
||||
scan_once
|
||||
done
|
||||
else
|
||||
echo "inotifywait not found; polling $folder every $poll_interval seconds" >&2
|
||||
while true; do
|
||||
sleep "$poll_interval"
|
||||
scan_once
|
||||
done
|
||||
fi
|
||||
Reference in New Issue
Block a user