Review fixes: at settle a certain answer ends the wait only when the lookup found a record or the answer is the inspector's or the recorded saver's; the legacy floor waits for the re-check. A record without a certain prompt ends the wait at once. Foreign writes need both save_foreign_runs and monitoring_enabled, read again just before the store. Dropped foreign files are logged outside the lock (evictions as evictions, due ones too), and a drained re-check never drops a newer reference.
132 lines
4.7 KiB
Python
132 lines
4.7 KiB
Python
"""The monitor's path for outputs of workflows without a PromptManager node (#247).
|
|
|
|
A foreign output (no positive PromptManager node in its graph) is decided by
|
|
the ladder the output scan uses (``utils.file_prompt.decide_file_prompt``)
|
|
with the ``MONITOR`` policy: certain files are stored at once, files only
|
|
recorded history can decide wait for their run's outputs to be sealed, and
|
|
everything else stays unlinked. This module holds the parts that do not
|
|
need the monitor's waiting sets: the setting, the decision call and the
|
|
store. The waiting and the re-check stay on ``ImageGenerationHandler``.
|
|
"""
|
|
|
|
import os
|
|
from dataclasses import dataclass
|
|
from typing import Any, Callable, Mapping, Optional, Tuple
|
|
|
|
from .file_prompt import PAIR, POSITIVE, Decision, Policy, decide_file_prompt
|
|
from .hashing import generate_prompt_hash
|
|
from .usage_tracking import survive_switch
|
|
|
|
try:
|
|
from ..database.pair_ingestion import RUN_MARKS, ingest_resolved_pair
|
|
from ..database.run_attribution import recorded_output_lookup
|
|
from ..database.scanned_prompts import store_scanned_prompt_or_raise
|
|
except ImportError:
|
|
from database.pair_ingestion import RUN_MARKS, ingest_resolved_pair
|
|
from database.run_attribution import recorded_output_lookup
|
|
from database.scanned_prompts import store_scanned_prompt_or_raise
|
|
|
|
# Decisions that name a prompt to store
|
|
CERTAIN = (PAIR, POSITIVE)
|
|
# Sources certain without a recorded run: the graph alone, or its recorded saver
|
|
ANCHORED_SOURCES = ("inspector", "recorded-run")
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ForeignRecheck:
|
|
"""``schedule`` snapshot: the one re-check of a foreign output after its
|
|
run's outputs were sealed. ``reference`` is the sealed output reference,
|
|
the spelling history recorded (``recorded_output_lookup(reference=)``)."""
|
|
|
|
reference: Mapping[str, Any]
|
|
|
|
|
|
def save_foreign_runs() -> bool:
|
|
"""Whether a foreign output may be stored now, read for every file.
|
|
|
|
``GalleryConfig.SAVE_FOREIGN_RUNS`` and ``MONITORING_ENABLED``: a
|
|
monitoring switch saved off stops foreign writes at once, although the
|
|
running observer watches until a restart. True when the configuration
|
|
cannot be imported (the defaults).
|
|
"""
|
|
try:
|
|
from ..py.config import GalleryConfig
|
|
except ImportError:
|
|
try:
|
|
from py.config import GalleryConfig
|
|
except ImportError:
|
|
return True
|
|
return (
|
|
GalleryConfig.SAVE_FOREIGN_RUNS is not False
|
|
and GalleryConfig.MONITORING_ENABLED is not False
|
|
)
|
|
|
|
|
|
def history_lookup(
|
|
db: Any, image_path: str, reference: Optional[Mapping[str, Any]] = None
|
|
) -> Callable[[], Any]:
|
|
"""The ladder's ``lookup`` for one file: its ``RecordedOutput`` or None.
|
|
|
|
With ``reference`` the file is spelled as history recorded it. Failures
|
|
are logged by ``recorded_output_lookup`` and name nothing.
|
|
"""
|
|
lookup = recorded_output_lookup(
|
|
db, reference=dict(reference) if reference is not None else None
|
|
)
|
|
return lambda: lookup(image_path)
|
|
|
|
|
|
def decide_foreign(
|
|
image_path: str,
|
|
metadata: Any,
|
|
*,
|
|
in_output: bool,
|
|
lookup: Optional[Callable[[], Any]],
|
|
) -> Decision:
|
|
"""The ``MONITOR`` ladder for one foreign output (the size is read only
|
|
when the ladder needs it)."""
|
|
|
|
def file_size() -> Optional[int]:
|
|
try:
|
|
return os.path.getsize(image_path)
|
|
except OSError:
|
|
return None
|
|
|
|
return decide_file_prompt(
|
|
metadata,
|
|
file_name=os.path.basename(image_path),
|
|
file_size=file_size,
|
|
in_output=in_output,
|
|
lookup=lookup,
|
|
policy=Policy.MONITOR,
|
|
)
|
|
|
|
|
|
def store_foreign(
|
|
db: Any, image_path: str, decision: Decision
|
|
) -> Tuple[str, Optional[int]]:
|
|
"""Store a certain decision: ``(status, prompt id)``.
|
|
|
|
One write through ``survive_switch``, so it runs again on the moved
|
|
library when a Move library switched while it waited (#249 S6b). Created
|
|
prompts carry ``RUN_MARKS``. A file linked to another prompt is a
|
|
``conflict`` and nothing is written. Database errors are raised.
|
|
"""
|
|
|
|
def store():
|
|
if decision.kind == PAIR:
|
|
result = ingest_resolved_pair(
|
|
db, image_path, decision.canonical, decision.resolution, marks=RUN_MARKS
|
|
)
|
|
return result["status"], result.get("image_id")
|
|
return store_scanned_prompt_or_raise(
|
|
db, image_path, decision.canonical, decision.text, RUN_MARKS
|
|
)
|
|
|
|
status, linked = survive_switch(db, store)
|
|
if decision.kind != PAIR or linked is None:
|
|
return status, linked
|
|
# The pair ingester reports the image row; its prompt is the positive's.
|
|
prompt = db.get_prompt_by_hash(generate_prompt_hash(decision.text))
|
|
return status, prompt["id"] if prompt else None
|