Major Features: - Backward compatibility: VALIDATE_INPUTS handles old boolean True/False values for enable_runtime_hooks parameter - Phase 3: XMP region metadata with MWG-RS compliance (faces, areas, coordinates, extensions) - Phase 4.4: Per-node UI toggle for runtime hooks (dropdown: Auto/Enabled/Disabled) - Runtime hooks enhancements: performance tracking, disagreement logging, configurable limits New Files: - config.json: Configuration file for runtime hooks and metadata settings - eric_metadata/hooks/runtime_capture.py: Runtime hook infrastructure - eric_metadata/utils/config.py: Configuration loader with priority system - eric_metadata/utils/version.py: Version helper for generated_by labels - eric_metadata/utils/a1111.py: Automatic1111 parameter string generation - eric_metadata/utils/jpeg_fallback.py: 4-stage JPEG metadata fallback - eric_metadata/workflow/: Workflow parsing and generation helpers - tests/test_backward_compatibility.py: 7 tests for parameter migration - tests/test_xmp_regions.py: 6 tests for XMP region round-trip - tests/test_per_node_runtime_hooks.py: 9 tests for per-node toggle Documentation: - CONFIG_GUIDE.md: Configuration options and usage - RUNTIME_HOOKS_QUICK_REFERENCE.md: Quick reference for runtime hooks - RUNTIME_HOOKS_ENHANCEMENTS_SUMMARY.md: Detailed enhancement documentation - SAFE_RUNTIME_HOOKS_RESEARCH.md: Safety analysis and validation - edit_log.md: Complete change history Test Results: All 143 tests passing - 7 backward compatibility tests - 6 XMP region tests - 9 per-node toggle tests - 17 runtime hooks enhancement tests - 104 existing tests Breaking Changes: None - fully backward compatible
6.1 KiB
6.1 KiB
Phase 2 BFS & Text Discovery API Design
Context
- Phase 1 landed graph-normalisation, inline asset aggregation, and sampler fallbacks. Tests confirm breadth-first traversal is stable for string-id graphs.
- Phase 2 targets richer discovery (sampler prioritisation + prompt/text harvesting) without introducing runtime hooks.
WorkflowParsingServicealready exposes:WorkflowGraph.bfs(start_node, direction="backward", max_depth=None)returning{node_id: distance}find_sampler_candidates(workflow, start_node, max_depth=6)returning orderedSamplerCandidateobjectsdiscover_prompt_nodes(workflow, start_node, max_depth=5, min_length=5)returningPromptNodeRefdataclasses
WorkflowMetadataProcessorconsumes the service to populategeneration.samplers,prompts, and inline asset metadata.
Goals
- Surface a reusable traversal API that captures both distance and class metadata for arbitrary nodes.
- Provide a text discovery helper that works with any node exposing strings (inputs or widgets), ranked by graph distance.
- Keep outputs serialisable (plain dict / list) so downstream consumers (save nodes, telemetry) can attach to metadata without new dependencies.
- Avoid regressions for workflows that still use integer node identifiers or sparse link definitions.
Proposed Additions
1. WorkflowParsingService.trace_from_node
class WorkflowParsingService:
def trace_from_node(
self,
workflow: RawWorkflow,
start_node: str,
*,
direction: str = "backward",
max_depth: Optional[int] = None,
) -> Dict[str, Dict[str, Any]]:
"""Return node_id -> payload with distance, class_type, and parent ids."""
- Wrapper over
WorkflowGraph.bfsthat enriches entries with:distance(int)class_type(string, default "")parents(list of upstream node ids whendirection="backward")
- Guarantees node ids are strings.
- Internally builds a
WorkflowGraphonce per call.
2. WorkflowParsingService.discover_text_nodes
class WorkflowParsingService:
def discover_text_nodes(
self,
workflow: RawWorkflow,
start_node: str,
*,
max_depth: int = 5,
min_length: int = 8,
) -> List[PromptNodeRef]:
"""Breadth-first search for text-bearing nodes near start_node."""
- Returns a list of dataclass instances (new
TextNodeRef) or plain dicts aligning with existingPromptNodeRefstructure:node_id,class_type,distance,is_negative(best-effort via_infer_negative_flag),texts(mapping field -> string),source("input" or "widget").
- Collects strings from:
- Node
inputswhere value isstrandlen(value) >= min_length. - Node
widgets_valueswhen present.
- Node
- Traversal respects
max_depth; nodes at greater depth are skipped. - Deduplicates fields per node; multiple fields per node remain in
textsdict. - Internally uses
_coerce_mappingand the normalized adjacency graph, so integer ids remain supported.
3. Metadata Processor Integration
- Enhance
_extract_detailed_promptsto:- Call
trace_from_nodeto record traversal info for debugging (workflow_info.node_distances). - Call
discover_text_nodesanchored at the resolved sampler node. - Populate
generation["prompt_nodes"]with ordered text node payloads (closest first). - Fallback to existing parsed prompts when traversal finds none.
- Call
- Add optional
discovery_modehandling: when enabled, append a short log entry for each text node discovered (node id, distance, text length) toself.discovery_log.
4. Data Contracts
- New dataclass (in
eric_metadata/workflow/parsing.py) for richer text discovery:
@dataclass
class TextNodeRef:
node_id: str
class_type: str
distance: int
is_negative: bool
texts: Dict[str, str]
sources: Dict[str, str] # field -> "input" | "widget"
PromptNodeRefcan either extend or aliasTextNodeRef; alternatively consolidate into one dataclass with optional fields to minimise duplication.- Serialized form (dict) used by metadata processor to keep outputs JSON-friendly.
Edge Cases & Safeguards
- Non-dict
workflow["prompt"]["nodes"]: continue to normalise via_coerce_mapping. - Cyclic graphs: BFS visited set prevents infinite traversal.
- Multi-sampler workflows:
find_sampler_candidatesstill the entry point; traversal kicks off using the chosen candidatenode_id. - Negative detection heuristic: reuse
_infer_negative_flagon each node’s inputs and class type. - Min length guard prevents capturing short numeric tokens (
len < min_length).
Testing Plan
- Unit Tests (
tests/prep/test_module_boundaries.pyadditions):test_trace_from_node_returns_parents_and_distance()test_discover_text_nodes_collects_inputs_and_widgets()test_discover_text_nodes_respects_depth_limit()test_metadata_processor_populates_prompt_nodes_from_discovery()
- Regression Scenarios:
- Workflows with integer node ids (already covered; extend tests to cover new APIs).
- Multi-sampler graph ensuring closest sampler wins.
- Nodes without text values should yield empty discovery results without errors.
- Performance Check: ensure traversal on mid-sized workflow (<150 nodes) completes <50ms in debug runs.
Open Questions
- Do we want to expose parent link indices (slot numbers) in
trace_from_nodefor richer diagnostics? (Default: no; can be added later.) - Should
discover_text_nodesallow custom filters (e.g., include numeric fields)? (Default: keep minimal; extend if user feedback demands.) - Where should we persist traversal traces when not in discovery mode? (
workflow_infovs dedicatedanalysis.tracebucket.)
Next Actions
- Implement dataclasses and helper APIs in
eric_metadata/workflow/parsing.pyper above. - Wire new helpers into
_extract_detailed_promptsinworkflow_metadata_processor.py. - Extend boundary tests and any downstream save-node formatting reliant on
generation["prompt_nodes"]ordering. - Update documentation (
plan-metadataEnhancementImplementation.prompt.md) with status once code lands.