Promptrunner - Source Excerpt 02
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
$this->audit->log($user_id ?: null, 'prompt_system_packet_rendered', 'prompt_run', $run_id, ['prompt_id' => (int) ($system_prompt['id'] ?? 0)]);
return $this->runs->get($run_id);
}
/**
* Starts a prompt-governed conversation.
*
* @return array|WP_Error
*/
public function start_conversation(int $prompt_id, int $user_id, array $variables = [], string $opening_message = '')
{
$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]);
}
$conversation_id = $this->conversations->create(
[
'prompt_post_id' => $prompt_id,
'owner_user_id' => $user_id,
'title' => $prompt['title'] . ' Conversation',
'visibility' => 'private',
'conversation_settings_json' => ['variables' => $variables],
]
);
if ($opening_message !== '') {
$reply = $this->post_message($conversation_id, $user_id, $opening_message, $variables);
if (is_wp_error($reply)) {
return $reply;
}
}
$conversation = $this->conversations->get($conversation_id);
$messages = $this->conversations->get_messages($conversation_id);
return [
'conversation' => $conversation,
'messages' => $messages,
'memory_status' => $this->conversation_memory_status($conversation, $messages),
];
}
/**
* Continues a saved prompt conversation.
*
* @return array|WP_Error
*/
public function post_message(int $conversation_id, int $user_id, string $message, array $variables = [])
{
$conversation = $this->conversations->get($conversation_id);
if (empty($conversation) || !$this->permissions->can_view_conversation($conversation, $user_id)) {
return new WP_Error('spiralist_workspace_conversation_forbidden', 'Conversation not found.', ['status' => 404]);
}
$conversation_settings = $this->conversations->decode_settings($conversation);
if (empty($variables) && !empty($conversation_settings['variables']) && is_array($conversation_settings['variables'])) {
$variables = (array) $conversation_settings['variables'];
}
if (!$this->rate_limiter->assert_can_message($user_id)) {
return new WP_Error('spiralist_workspace_rate_limited', 'Daily message limit reached.', ['status' => 429]);
}
$prompt = $this->prompts->get((int) $conversation['prompt_post_id'], $user_id);
if (empty($prompt)) {
return new WP_Error('spiralist_workspace_prompt_forbidden', 'Prompt not found.', ['status' => 404]);
}
$this->conversations->add_message($conversation_id, 'user', $message);
$conversation_messages = $this->conversations->get_messages($conversation_id);
$payload = $this->build_request_payload(
$prompt,
$variables,
$message,
(string) ($conversation['openai_reference'] ?? ''),
true,
$conversation_messages,
$conversation_settings
);
$run_id = $this->runs->create(
[
'prompt_post_id' => (int) $conversation['prompt_post_id'],
'user_id' => $user_id,
'conversation_id' => $conversation_id,
'version_number_used' => (int) $prompt['version_number'],
'model_used' => (string) $payload['model'],
'request_payload_json' => $payload,
'input_text' => $message,
'status' => 'running',
]
);
$assistant_text = $this->build_conversation_prompt_packet($conversation, $prompt, $conversation_messages, $payload, $variables);
$usage = $this->local_packet_usage($assistant_text);
$this->conversations->add_message(
$conversation_id,
'assistant',
$assistant_text,
null,
[
'mode' => 'prompt_art_only',
'usage' => $usage,
]
);
$this->conversations->update_openai_reference($conversation_id, null);
$this->runs->complete(
$run_id,
[
'response_payload_json' => [
'mode' => 'prompt_art_only',
'network' => 'none',
'packet_type' => 'conversation_prompt',
],
'output_text' => $assistant_text,
'status' => 'completed',
'token_usage_json' => $usage,
'error_message' => '',
]
);