Search by

cleatsquad / php-prompt-policy

mimou78

Situational prompt composition and policy engine for PHP

Package info

github.com/CleatSquad/php-prompt-policy

pkg:composer/cleatsquad/php-prompt-policy

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 2

v1.0.0 2026-08-27 07:07 UTC

This package is not auto-updated.

Last update: 2026-08-28 05:27:50 UTC


README

Situational prompt composition for PHP. A prompt is built from independent policies, each deciding on its own whether it has something to say about the current turn — instead of one function growing a new if per situation.

MIT · PHP >= 8.4 · no runtime dependency

The problem

A system prompt assembled by hand becomes a single function that knows about memory, language, capabilities, conversation history and identity at once. Every new situation adds a branch, the branches start to contradict each other, and nothing states which instruction wins when two apply.

This package splits that function into policies. Each one answers two questions about a context object it does not own — does this concern me? and what do I contribute? — and the engine assembles what comes back, in a defined order.

Install

composer require cleatsquad/php-prompt-policy

Use

A policy implements three methods. The context is any object: the package never inspects it, so the shape belongs to the caller.

use Cleatsquad\PromptPolicy\PromptPolicyInterface;
use Cleatsquad\PromptPolicy\PromptSection;

final readonly class LanguagePolicy implements PromptPolicyInterface
{
    public function applies(object $context): bool
    {
        return $context->language !== null;
    }

    public function priority(): int
    {
        return 20;
    }

    public function render(object $context): ?PromptSection
    {
        return new PromptSection(
            content: "Answer in {$context->language}.",
            name: 'language',
        );
    }
}

Register the policies and evaluate one context:

use Cleatsquad\PromptPolicy\PromptPolicyEngine;

$engine = new PromptPolicyEngine([
    new IdentityPolicy(),
    new MemoryPolicy(),
    new LanguagePolicy(),
]);

$sections = $engine->evaluate($turnContext);

$prompt = implode("\n\n", array_map(
    static fn (PromptSection $section): string => $section->content,
    $sections,
));

evaluate() returns a list<PromptSection> sorted by descending priority. A policy contributes nothing when applies() is false, or when render() returns null — the second case covers a policy that concerns the turn but finds it has no content for it.

API

Type Role
PromptPolicyInterface applies(), priority(), render() — one situation, one implementation
PromptSection readonly value object: content, name
PromptPolicyEngine holds the policies, register() adds one, evaluate() returns the ordered sections

Ordering is deterministic: same policies and same context produce the same sections in the same order, which is what makes a composed prompt testable byte for byte. PromptPolicyInterface::priority() is the single source of truth for that order — PromptPolicyEngine sorts on it directly, and PromptSection carries no priority of its own.

Test

composer install
composer test      # PHPUnit
composer analyse   # PHPStan, max level

License

MIT. See LICENSE.