Pair category repair (#550) and saved instructions + dialog picker (#431) (#590)

* feat(maintenance): repair old Pair prompts' empty category and tags (#550)

Squashed from PR #579. Closes #550.

* feat(tagging): saved instructions with a default per engine (#431)

Squashed from PR #587.

* feat(tagging): pick saved instructions in the tagging dialogs (#431)

Squashed from PR #588. Closes #431.
This commit is contained in:
Vito
2026-11-01 03:01:07 +00:00
committed by GitHub
parent 9640ca86f8
commit 3295ff8b20
44 changed files with 3905 additions and 207 deletions
+60 -6
View File
@@ -1,8 +1,9 @@
"""Maintenance routes: reviewed, read-only plans (#249 System S6).
``POST /prompt_manager/maintenance/preview {operation, options:{}}`` reviews
one operation (``duplicate_prompts``, ``optimize``, ``unused_components`` or
``missing_links``; ``database/maintenance.py``; ``duplicate_files``,
one operation (``duplicate_prompts``, ``optimize``, ``unused_components``,
``missing_links`` or ``pair_metadata``, #550; ``database/maintenance.py``;
``duplicate_files``,
``py/api/duplicate_files.py``; ``rotated_logs``, the rotated log copies,
``py/api/log_cleanup.py``, which has its own apply job) off the event loop and
answers a ``Plan``: ``{success, token, operation, expires_at, scope:{count,
@@ -97,7 +98,11 @@ MAINTENANCE_TITLES = {
"unused_components": "Unused prompt components",
"missing_links": "Missing-file links",
"workflow_storage": "Store each workflow once",
# #550, beyond the concept: fills old Pair prompts' empty category and tags.
"pair_metadata": "Pair category and tags",
}
# Operations whose job keeps every changed row for its report (#550).
COMPLETE_RECORD = frozenset({"pair_metadata"})
_OPERATION_ERROR = "operation must be one of: " + ", ".join(PREVIEW_OPERATIONS)
MAINTENANCE_GROUPS = ("changed", "protected", "errors")
MAX_TOKEN_LENGTH = 128
@@ -281,9 +286,27 @@ def short_space_message(short):
)
def _changed_line(operation, count, marks=0):
def _pair_metadata_line(result):
count = result["changed"]
noun = (
"prompt from its saved pairs"
if count == 1
else "prompts from their saved pairs"
)
categories = result.get("categories", 0)
tag_sets = result.get("tag_sets", 0)
return (
f"Filled {count:,} {noun} "
f"({categories:,} {'category' if categories == 1 else 'categories'}, "
f"{tag_sets:,} {'tag set' if tag_sets == 1 else 'tag sets'})"
)
def _changed_line(operation, count, marks=0, result=None):
if operation == "optimize":
return "Optimized the database"
if operation == "pair_metadata":
return _pair_metadata_line(result or {"changed": count})
if operation == "duplicate_prompts":
noun = "duplicate prompt" if count == 1 else "duplicate prompts"
return f"Merged {count:,} {noun}"
@@ -668,9 +691,28 @@ class MaintenanceRoutesMixin:
rows = await self._run_in_executor(
self._maintenance_rows, plan.operation, result["rows"]
)
job.record_counted("changed", result["changed"], rows)
job.record_counted("protected", payload["protected"])
line = _changed_line(plan.operation, result["changed"], result.get("marks", 0))
job.record_counted(
"changed",
result["changed"],
rows,
complete=plan.operation in COMPLETE_RECORD,
)
if "kept" in result:
# #550: each kept record with its reason, like the changed ones.
kept = [
{
"path": "",
"detail": f"Prompt {item['prompt_id']}: kept, "
+ "; ".join(item["reasons"]),
}
for item in result["kept"]
]
job.record_counted("protected", len(kept), kept, complete=True)
else:
job.record_counted("protected", payload["protected"])
line = _changed_line(
plan.operation, result["changed"], result.get("marks", 0), result
)
job.log_line(line)
job.update(progress=100, status_line=f"{line} · {backup_note(backup)}")
return None
@@ -691,6 +733,8 @@ class MaintenanceRoutesMixin:
}
)
return rows
if operation == "pair_metadata":
return [{"path": "", "detail": _filled_detail(item)} for item in described]
if operation == "duplicate_prompts":
return [
{
@@ -708,6 +752,16 @@ class MaintenanceRoutesMixin:
]
def _filled_detail(item):
"""``Prompt 12: category “portrait”; tags a, b``: one filled prompt (#550)."""
parts = []
if "category" in item:
parts.append(f"category “{item['category']}”")
if item.get("tags"):
parts.append("tags " + ", ".join(item["tags"]))
return f"Prompt {item['prompt_id']}: " + "; ".join(parts)
def backup_note(backup):
"""The status line's backup part: the file name, or that there is none."""
return f"Safety backup {backup}" if backup else NO_BACKUP