feat(runs): state machine, idempotent resume, reaper extension, retention
Pipeline v2 Phase 1, commit 4/4. Run_Stage_State persists per-run per-stage state keyed run_id + stage + agent_role + item_key (item_key scopes articles because one run_id spans a whole batch; agent_role is the Phase-2 quorum dimension, schema-only). Done stages snapshot output and are reused on re-entry — writer stages via Content_Generator::set_run_item, the review via its stored verdict, a published item skipped outright — never re-run, never re-charged. Post creation is keyed by _prautoblogger_run_id + new _prautoblogger_idea_hash (check-before-insert in Publisher) so retries cannot duplicate posts. Run-level research/analysis checkpoints + done/failed lifecycle marks ride Pipeline_Runner/Executor (incl. orphan-abort + budget-abort paths). Run_Reaper rides the EXISTING #19 daily cron: stages stuck running >2x expected stage wall-clock and runs silent >2x expected run wall-clock -> failed (filterable), open stages reaped, queryable as 'incomplete'. Retention: request_json + stage output payloads NULLed after R days, R from the new Audit Payload Retention setting (default constant 14, 0 = keep forever, no literal at point of use). Audit_Writer records editorial verdicts (run_decisions) and per-article kept sources (run_sources) for new runs. Under-300: status-transient/summary helpers extracted verbatim to Pipeline_Status (Pipeline_Runner 271; removed the worker's duplicate). Agent-Session: pipeline-v2-phase1-engineer-20260612
This commit is contained in:
@@ -19,6 +19,12 @@ class PRAutoBlogger_Content_Generator {
|
||||
private PRAutoBlogger_LLM_Provider_Interface $llm;
|
||||
private PRAutoBlogger_Cost_Tracker $cost_tracker;
|
||||
|
||||
/** @var string|null Run UUID for stage-state checkpointing (null = off). */
|
||||
private ?string $run_id = null;
|
||||
|
||||
/** @var string|null Stage item key scoping this article within the run. */
|
||||
private ?string $item_key = null;
|
||||
|
||||
public function __construct(
|
||||
PRAutoBlogger_LLM_Provider_Interface $llm,
|
||||
PRAutoBlogger_Cost_Tracker $cost_tracker
|
||||
@@ -27,6 +33,25 @@ class PRAutoBlogger_Content_Generator {
|
||||
$this->cost_tracker = $cost_tracker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable per-stage state checkpointing for one article item (v0.18.0).
|
||||
*
|
||||
* When set, every LLM stage (outline/draft/polish or the single-pass
|
||||
* draft) records pending->running->done state and snapshots its output;
|
||||
* a re-entered done stage returns the snapshot WITHOUT a new LLM call
|
||||
* (never re-charged). When unset (default), behavior is exactly
|
||||
* pre-v0.18.0 — used by callers outside a run (e.g. eval mode).
|
||||
*
|
||||
* @param string|null $run_id Run UUID, or null to disable.
|
||||
* @param string|null $item_key Item key from Run_Stage_State::item_key_for_idea().
|
||||
* @return $this
|
||||
*/
|
||||
public function set_run_item( ?string $run_id, ?string $item_key ): self {
|
||||
$this->run_id = $run_id;
|
||||
$this->item_key = $item_key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a blog post from an article idea.
|
||||
*
|
||||
@@ -159,6 +184,19 @@ class PRAutoBlogger_Content_Generator {
|
||||
array $options,
|
||||
?string $prompt_key = null
|
||||
): string {
|
||||
// Idempotent resume: a done stage returns its snapshot, no LLM call.
|
||||
if ( null !== $this->run_id && null !== $this->item_key ) {
|
||||
$cached = PRAutoBlogger_Run_Stage_State::get_output( $this->run_id, $stage, '', $this->item_key );
|
||||
if ( null !== $cached ) {
|
||||
PRAutoBlogger_Logger::instance()->info(
|
||||
sprintf( 'Reusing completed %s stage output (idempotent resume).', $stage ),
|
||||
'pipeline'
|
||||
);
|
||||
return $cached;
|
||||
}
|
||||
PRAutoBlogger_Run_Stage_State::start( $this->run_id, $stage, '', $this->item_key );
|
||||
}
|
||||
|
||||
$ctx = PRAutoBlogger_Opik_Trace_Context::current();
|
||||
|
||||
$span_id = $ctx->start_span(
|
||||
@@ -209,6 +247,17 @@ class PRAutoBlogger_Content_Generator {
|
||||
$prompt_key
|
||||
);
|
||||
|
||||
if ( null !== $this->run_id && null !== $this->item_key ) {
|
||||
PRAutoBlogger_Run_Stage_State::done(
|
||||
$this->run_id,
|
||||
$stage,
|
||||
'',
|
||||
$this->item_key,
|
||||
(string) $response['content'],
|
||||
$this->llm->estimate_cost( $response['model'], (int) $response['prompt_tokens'], (int) $response['completion_tokens'] )
|
||||
);
|
||||
}
|
||||
|
||||
return $response['content'];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user