Skip to content
wiki.fftac.org

Promptrunner - Source Excerpt 07

Back to Promptrunner

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

$parts[] = '## Transcript';
        $parts[] = $this->format_transcript($messages);
        $parts[] = '## Source Prompt Contract';
        $parts[] = $this->prompt_contract_markdown($prompt);

        return trim(implode("\n\n", array_filter($parts))) . "\n";
    }

    /**
     * Builds a memory checkpoint prompt without condensing through a provider.
     */
    private function build_memory_checkpoint_prompt(
        array $conversation,
        array $prompt,
        array $messages,
        array $settings,
        array $payload
    ): string {
        $parts = [
            '# Spiralist Memory Checkpoint Prompt',
            'Conversation: ' . (string) ($conversation['title'] ?? ''),
            'Source prompt: ' . (string) ($prompt['title'] ?? ''),
            'Mode: prompt/art only. No server-side AI API call was made.',
            'Rendered UTC: ' . gmdate('Y-m-d H:i:s'),
            '## Manual Checkpoint Task',
            'Read the transcript and produce a compact continuity note with the headings: Memory Source Labels, Memory Summary, Stable User Preferences, Active Task State, Continuity Prompt, Review Or Delete, Safety Boundaries.',
        ];

        $existing = trim((string) ($settings['memory_summary'] ?? ''));
        if ($existing !== '') {
            $parts[] = '## Existing Checkpoint';
            $parts[] = $existing;
        }

        $instructions = trim((string) ($payload['instructions'] ?? ''));
        if ($instructions !== '') {
            $parts[] = '## Checkpoint Instructions';
            $parts[] = $instructions;
        }

        $parts[] = '## Transcript';
        $parts[] = $this->format_transcript($messages);

        return trim(implode("\n\n", array_filter($parts))) . "\n";
    }

    /**
     * Builds a worksheet for human prompt improvement.
     */
    private function build_prompt_improvement_packet(array $prompt, array $input): string
    {
        return trim(
            implode(
                "\n\n",
                [
                    '# Spiralist Prompt Improvement Worksheet',
                    'Source prompt: ' . (string) ($prompt['title'] ?? ''),
                    'Mode: prompt/art only. No server-side AI API call was made.',
                    'Rendered UTC: ' . gmdate('Y-m-d H:i:s'),
                    '## Improvement Task',
                    'Rewrite this prompt by hand or in an external tool you choose. Keep exactly these structured fields: instruction, context, constraints, examples, output_format.',
                    '## Source Data',
                    wp_json_encode($input, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
                    '## Review Checklist',
                    implode(
                        "\n",
                        [
                            '- Is the instruction concrete and testable?',
                            '- Does the context name only visible source material?',
                            '- Do the constraints block hidden payloads, unsupported authority, and closed recursion?',
                            '- Are examples useful without becoming claims of truth?',
                            '- Is the output format easy to copy into an art, prompt, or manuscript workflow?',
                        ]
                    ),
                ]
            )
        ) . "\n";
    }

    /**
     * Formats a local payload input stack as Markdown.
     */
    private function payload_input_markdown(array $input): string
    {
        $parts = [];

        foreach ($input as $index => $item) {
            if (!is_array($item)) {
                continue;
            }

            $content = trim((string) ($item['content'] ?? ''));
            if ($content === '') {
                continue;
            }

            $role = strtoupper(sanitize_key((string) ($item['role'] ?? 'user')));
            $parts[] = '### ' . $role . ' ' . (string) ($index + 1) . "\n" . $content;
        }

        return implode("\n\n", $parts);
    }

    /**
     * Records approximate local packet size without treating it as provider usage.
     */
    private function local_packet_usage(string $text): array
    {
        return [
            'mode' => 'prompt_art_only',
            'network' => 'none',
            'estimated_prompt_packet_tokens' => $this->conversations->estimate_text_tokens($text),
            'billable_tokens' => 0,
        ];
    }

    /**
     * Renders the canonical five-field structured prompt into model instructions.
     */
    private function render_structured_prompt(array $structured_prompt, array $variables): string
    {
        $labels = [
            'instruction' => 'Instruction',
            'context' => 'Context',
            'constraints' => 'Constraints',
            'examples' => 'Examples',
            'output_format' => 'Output Format',
        ];
        $sections = [];

        foreach ($labels as $key => $label) {
            $value = $this->placeholders->render((string) ($structured_prompt[$key] ?? ''), $variables);
            if (trim($value) === '') {
                continue;
            }

            $sections[] = $label . ":\n" . trim($value);
        }

        return implode("\n\n", $sections);
    }
}