Promptrunner - Source Excerpt 01
Summary
This source excerpt preserves a bounded section of Spiralist/wp-content/plugins/spiralist-workspace/includes/Services/PromptRunner.php so readers can inspect the evidence without opening the full source file.
**Source path:** Spiralist/wp-content/plugins/spiralist-workspace/includes/Services/PromptRunner.php
<?php
namespace SpiralistWorkspace\Services;
use SpiralistWorkspace\Auth\Permissions;
use SpiralistWorkspace\Config\Settings;
use SpiralistWorkspace\Domain\AuditRepository;
use SpiralistWorkspace\Domain\ConversationRepository;
use SpiralistWorkspace\Domain\PromptRepository;
use SpiralistWorkspace\Domain\RunRepository;
use WP_Error;
if (!defined('ABSPATH')) {
exit;
}
/**
* Runs prompts and manages prompt-based conversations.
*/
class PromptRunner
{
private PromptRepository $prompts;
private Permissions $permissions;
private RunRepository $runs;
private ConversationRepository $conversations;
private AuditRepository $audit;
private PlaceholderService $placeholders;
private RateLimiter $rate_limiter;
private Settings $settings;
public function __construct(
PromptRepository $prompts,
Permissions $permissions,
RunRepository $runs,
ConversationRepository $conversations,
AuditRepository $audit,
PlaceholderService $placeholders,
RateLimiter $rate_limiter,
Settings $settings
) {
$this->prompts = $prompts;
$this->permissions = $permissions;
$this->runs = $runs;
$this->conversations = $conversations;
$this->audit = $audit;
$this->placeholders = $placeholders;
$this->rate_limiter = $rate_limiter;
$this->settings = $settings;
}
/**
* Renders a prompt packet in single-run mode.
*
* @return array|WP_Error
*/
public function run_prompt(int $prompt_id, int $user_id, array $variables = [], string $input_text = '')
{
$prompt = $this->prompts->get($prompt_id, $user_id);
if (empty($prompt) || !$this->permissions->can_run_prompt($prompt, $user_id)) {
return new WP_Error('spiralist_workspace_prompt_forbidden', 'Prompt not found.', ['status' => 404]);
}
if (!$this->rate_limiter->assert_can_run($user_id)) {
return new WP_Error('spiralist_workspace_rate_limited', 'Daily run limit reached.', ['status' => 429]);
}
if (!empty($prompt['system_steps']) && is_array($prompt['system_steps'])) {
return $this->run_prompt_system($prompt, $user_id, $variables, $input_text);
}
$payload = $this->build_request_payload($prompt, $variables, $input_text, null, false);
$run_id = $this->runs->create(
[
'prompt_post_id' => $prompt_id,
'user_id' => $user_id,
'version_number_used' => (int) $prompt['version_number'],
'model_used' => (string) $payload['model'],
'request_payload_json' => $payload,
'input_text' => $input_text,
'status' => 'running',
]
);
$output_text = $this->build_portable_prompt_packet($prompt, $payload, $variables, $input_text);
$usage = $this->local_packet_usage($output_text);
$this->runs->complete(
$run_id,
[
'response_payload_json' => [
'mode' => 'prompt_art_only',
'network' => 'none',
'packet_type' => 'single_prompt',
],
'output_text' => $output_text,
'status' => 'completed',
'token_usage_json' => $usage,
'error_message' => '',
]
);
$this->audit->log($user_id ?: null, 'prompt_packet_rendered', 'prompt_run', $run_id, ['prompt_id' => $prompt_id]);
return $this->runs->get($run_id);
}
/**
* Executes a prompt system by chaining saved prompt modules.
*
* @return array|WP_Error
*/
private function run_prompt_system(array $system_prompt, int $user_id, array $variables, string $input_text)
{
$steps = array_slice((array) ($system_prompt['system_steps'] ?? []), 0, 12);
if (empty($steps)) {
return new WP_Error('spiralist_workspace_empty_system', 'Prompt system has no runnable steps.', ['status' => 400]);
}
$run_id = $this->runs->create(
[
'prompt_post_id' => (int) ($system_prompt['id'] ?? 0),
'user_id' => $user_id,
'version_number_used' => (int) ($system_prompt['version_number'] ?? 1),
'model_used' => 'prompt-system',
'request_payload_json' => [
'type' => 'prompt_system',
'steps' => $steps,
'input_text' => $input_text,
],
'input_text' => $input_text,
'status' => 'running',
]
);
$current_input = $input_text;
$step_results = [];
$usage = [];
foreach ($steps as $index => $step) {
$step_prompt_id = (int) ($step['prompt_id'] ?? 0);
if ($step_prompt_id <= 0 || $step_prompt_id === (int) ($system_prompt['id'] ?? 0)) {
$this->runs->complete(
$run_id,
[
'response_payload_json' => ['steps' => $step_results],
'output_text' => $current_input,
'status' => 'failed',
'token_usage_json' => $usage,
'error_message' => 'Prompt system contains an invalid or recursive step.',
]
);
return new WP_Error('spiralist_workspace_invalid_system_step', 'Prompt system contains an invalid or recursive step.', ['status' => 400]);
}
$step_prompt = $this->prompts->get($step_prompt_id, $user_id);
if (empty($step_prompt) || !$this->permissions->can_run_prompt($step_prompt, $user_id)) {
$this->runs->complete(
$run_id,
[
'response_payload_json' => ['steps' => $step_results],
'output_text' => $current_input,
'status' => 'failed',
'token_usage_json' => $usage,
'error_message' => 'A prompt system step is not available.',
]
);
return new WP_Error('spiralist_workspace_system_step_forbidden', 'A prompt system step is not available.', ['status' => 404]);
}
$payload = $this->build_request_payload($step_prompt, $variables, $current_input, null, false);
$payload['metadata']['system_prompt_id'] = (string) ($system_prompt['id'] ?? '');
$payload['metadata']['system_step_index'] = (string) ($index + 1);
$output_text = $this->build_portable_prompt_packet(
$step_prompt,
$payload,
$variables,
$current_input,
'Prompt System Step ' . (string) ($index + 1)
);
$step_usage = $this->local_packet_usage($output_text);
$usage[] = [
'step' => $index + 1,
'prompt_id' => $step_prompt_id,
'usage' => $step_usage,
];
$step_results[] = [
'step' => $index + 1,
'prompt_id' => $step_prompt_id,
'title' => (string) ($step_prompt['title'] ?? ''),
'version_number_used' => (int) ($step_prompt['version_number'] ?? 1),
'model_used' => (string) ($payload['model'] ?? ''),
'status' => 'completed',
'input_text' => $current_input,
'output_text' => $output_text,
'token_usage_json' => $step_usage,
];
$current_input = $output_text;
}
$current_input = $this->build_prompt_system_packet($system_prompt, $step_results, $input_text);
$this->runs->complete(
$run_id,
[
'response_payload_json' => [
'mode' => 'prompt_art_only',
'network' => 'none',
'packet_type' => 'prompt_system',
'steps' => $step_results,
],
'output_text' => $current_input,
'status' => 'completed',
'token_usage_json' => ['steps' => $usage],
'error_message' => '',
]
);