- Dossier_Actions (4 AJAX endpoints, manage_options+nonce): save fork / queue replay / queue rebuild / stage-status poll. Run identity always derived server-side from post meta; the AJAX layer validates + queues ONLY — all mutations happen in the cron handlers (chained-cron, CPO hard constraint). Fork saves lock message structure (count/roles) so every version stays replayable. - Edit panel per editable stage: per-message textareas prefilled from the latest fork or the recorded request_json; explicit fork + downstream-stale + queued-execution copy; spend strip with ceiling warning (filterable fraction, default 0.8). Disabled stages always show WHY (policy reasons; missing-input retention copy). - Stale / human-edited / attempt chips per stage + header badges (M2 slot filled); dossier polls stage status while a re-run is active (board poll interval setting) and re-renders when the run settles. - F2: sidebar Models & Prompts card (per-stage model/pv/role) + Run Spend card. F3: log-only stages (image_a/b, llm_research, ...) render from generation_log + the _prautoblogger_image_role attachment grid. - M2 correctness: dossier is now item-scoped (idea-hash meta) and excludes gen_log rows linked to other posts of a multi-article run. - Run-list visibility (product AC): board cards get a Human-modified chip; Review Queue rows get a stale-stages chip linking to the dossier. Both via single batched queries (no N+1). - v0.20.0 + CHANGELOG + ARCHITECTURE #24 (+ file tree, schema tables, db-version note) in this commit per versioning discipline. Agent-Session: engineer-prab-m3-20260612
119 lines
5.1 KiB
PHP
119 lines
5.1 KiB
PHP
<?php
|
|
/**
|
|
* Partial: log-only stage section (M3/F3, v0.20.0).
|
|
*
|
|
* Renders stages that exist ONLY in generation_log — image_a/image_b,
|
|
* image_prompt_rewrite, llm_research, opik_eval_judge, ... — which the
|
|
* M2 dossier silently dropped because the image pipeline (and the
|
|
* research providers) never write run_stages rows (QA M2 F3). These
|
|
* sections show the call record (model, tokens, cost, status, raw
|
|
* trace) from their REAL data source; image stages additionally render
|
|
* the post's actual pipeline attachments ($dossier['images']).
|
|
*
|
|
* Variables injected by dossier-page.php:
|
|
* $stage_name (string) -- stage key
|
|
* $log_rows (array) -- generation_log rows for this stage
|
|
* $dossier (array) -- page view model (images)
|
|
*
|
|
* Security: identical escaping discipline to dossier-stage-section.php.
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
$lo_label = PRAutoBlogger_Stage_Display_Map::label( $stage_name );
|
|
$lo_is_image = in_array( $stage_name, array( 'image_a', 'image_b' ), true );
|
|
$lo_images = $lo_is_image && ! empty( $dossier['images'] ) ? (array) $dossier['images'] : array();
|
|
$lo_cost = 0.0;
|
|
$lo_status = '';
|
|
$lo_model = '—';
|
|
foreach ( $log_rows as $lo_row ) {
|
|
$lo_cost += (float) ( $lo_row['estimated_cost'] ?? 0 );
|
|
$lo_status = (string) ( $lo_row['response_status'] ?? '' );
|
|
if ( ! empty( $lo_row['model'] ) ) {
|
|
$lo_model = (string) $lo_row['model'];
|
|
}
|
|
}
|
|
$lo_section_id = 'prab-logstage-' . esc_attr( $stage_name );
|
|
?>
|
|
<div class="prab-dossier-section prab-dossier-section--stage prab-dossier-section--log-only"
|
|
data-stage="<?php echo esc_attr( $stage_name ); ?>"
|
|
id="<?php echo $lo_section_id; // Already escaped. ?>">
|
|
|
|
<div class="prab-stage-header">
|
|
<h2 class="prab-section-title"><?php echo esc_html( $lo_label ); ?></h2>
|
|
<div class="prab-stage-header-meta">
|
|
<?php if ( '' !== $lo_status ) : ?>
|
|
<span class="prab-trace-status prab-trace-status--<?php echo esc_attr( $lo_status ); ?>">
|
|
<?php echo esc_html( $lo_status ); ?>
|
|
</span>
|
|
<?php endif; ?>
|
|
<?php if ( $lo_cost > 0 ) : ?>
|
|
<span class="prab-stage-cost">$<?php echo esc_html( number_format( $lo_cost, 5 ) ); ?></span>
|
|
<?php endif; ?>
|
|
<span class="prab-stage-model"><?php echo esc_html( $lo_model ); ?></span>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if ( ! empty( $lo_images ) ) : ?>
|
|
<div class="prab-image-grid">
|
|
<?php foreach ( $lo_images as $lo_image ) : ?>
|
|
<figure class="prab-image-card<?php echo ! empty( $lo_image['is_featured'] ) ? ' prab-image-card--chosen' : ''; ?>">
|
|
<img src="<?php echo esc_url( (string) $lo_image['url'] ); ?>" alt="<?php echo esc_attr( (string) $lo_image['role'] ); ?>" loading="lazy" />
|
|
<figcaption>
|
|
<?php echo esc_html( ucfirst( (string) $lo_image['role'] ) ); ?>
|
|
<?php if ( ! empty( $lo_image['is_featured'] ) ) : ?>
|
|
<span class="prab-chip prab-chip--chosen"><?php esc_html_e( 'Chosen (featured)', 'prautoblogger' ); ?></span>
|
|
<?php endif; ?>
|
|
</figcaption>
|
|
</figure>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php elseif ( $lo_is_image ) : ?>
|
|
<p class="prab-output-absent"><?php esc_html_e( 'No pipeline attachments recorded for this article (images attach on publish).', 'prautoblogger' ); ?></p>
|
|
<?php endif; ?>
|
|
|
|
<div class="prab-stage-trace">
|
|
<button type="button" class="prab-trace-toggle button-link"
|
|
aria-expanded="false"
|
|
aria-controls="<?php echo $lo_section_id; // Already escaped. ?>-trace">
|
|
<?php esc_html_e( 'Show raw trace', 'prautoblogger' ); ?>
|
|
</button>
|
|
<div id="<?php echo $lo_section_id; // Already escaped. ?>-trace" class="prab-trace-detail" hidden>
|
|
<?php foreach ( $log_rows as $log_row ) : ?>
|
|
<div class="prab-trace-entry">
|
|
<div class="prab-trace-meta">
|
|
<span><?php echo esc_html( (string) ( $log_row['model'] ?? '—' ) ); ?></span>
|
|
<span>
|
|
<?php
|
|
printf(
|
|
/* translators: 1: prompt tokens, 2: completion tokens. */
|
|
esc_html__( '%1$d prompt + %2$d completion tokens', 'prautoblogger' ),
|
|
(int) ( $log_row['prompt_tokens'] ?? 0 ),
|
|
(int) ( $log_row['completion_tokens'] ?? 0 )
|
|
);
|
|
?>
|
|
</span>
|
|
<span>$<?php echo esc_html( number_format( (float) ( $log_row['estimated_cost'] ?? 0 ), 6 ) ); ?></span>
|
|
<span class="prab-trace-status prab-trace-status--<?php echo esc_attr( (string) ( $log_row['response_status'] ?? '' ) ); ?>">
|
|
<?php echo esc_html( (string) ( $log_row['response_status'] ?? '' ) ); ?>
|
|
</span>
|
|
</div>
|
|
<?php if ( ! empty( $log_row['request_json'] ) ) : ?>
|
|
<details class="prab-trace-request">
|
|
<summary><?php esc_html_e( 'Request payload', 'prautoblogger' ); ?></summary>
|
|
<?php // SECURITY: esc_html() treats model output as untrusted HTML. ?>
|
|
<pre class="prab-trace-pre"><?php echo esc_html( (string) $log_row['request_json'] ); ?></pre>
|
|
</details>
|
|
<?php endif; ?>
|
|
<?php if ( ! empty( $log_row['error_message'] ) ) : ?>
|
|
<p class="prab-trace-error"><?php echo esc_html( (string) $log_row['error_message'] ); ?></p>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
|
|
</div><!-- .prab-dossier-section--log-only -->
|