> ## Documentation Index
> Fetch the complete documentation index at: https://deck.promptphp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Working with prompts

> Load, render, version, cache, and track AI prompts in your Laravel application.

## Introduction

Deck by PromptPHP provides a clean, expressive API for loading, rendering, and managing versioned AI prompts in your Laravel application. Prompts are stored as plain files on disk and organised by name, version, and role. They are accessed through the `Deck` facade.

A prompt can contain multiple **roles** (system, user, assistant, developer, tool, or any custom role you define). Each role's content supports `{{ $variable }}` interpolation, and the entire prompt can be converted into a messages array ready to send to OpenAI, Anthropic, or any chat-completion API.

```php theme={null}
use PromptPHP\Deck\Facades\Deck;

$prompt = Deck::get('order-summary');

$prompt->system(['tone' => 'friendly']);
// "You are a friendly AI assistant..."

$prompt->user(['input' => $request->message]);
// "Summarise the following order: ..."
```

## Retrieving prompts

### The Deck facade

The `Deck` facade is the primary entry point for loading prompts. It delegates to the `PromptManager` singleton registered by the service provider:

```php theme={null}
use PromptPHP\Deck\Facades\Deck;

// Load the active version.
$prompt = Deck::get('order-summary');

// Load a specific version.
$prompt = Deck::get('order-summary', 2);
```

The `get` method returns a `PromptTemplate` instance. If no version is specified, the [active version](#version-resolution-order) is resolved automatically.

### Dependency injection

You can also inject the `PromptManager` directly via Laravel's service container:

```php theme={null}
use PromptPHP\Deck\PromptManager;

class OrderController extends Controller
{
    public function __construct(protected PromptManager $prompts) {}

    public function summarise(Request $request)
    {
        $prompt = $this->prompts->get('order-summary');
        // ...
    }
}
```

The `PromptManager` is registered as a singleton, so the same instance is reused throughout the request lifecycle.

### Active version

Use the `active` method to explicitly load the active version:

```php theme={null}
$prompt = Deck::active('order-summary');

$prompt->version(); // e.g. 3
```

This is equivalent to calling `get()` without a version number.

### Specific version

Pass a version number as the second argument to `get` to load a specific version, regardless of which version is currently active:

```php theme={null}
$prompt = Deck::get('order-summary', 1);

$prompt->version(); // 1
```

If the version does not exist, an `InvalidVersionException` is thrown.

## Rendering roles

Once you have a `PromptTemplate` instance, you can render any role's content with variable interpolation.

### Dynamic role methods

The most expressive way to render a role is to call it as a method directly on the prompt instance. This uses PHP's `__call` magic method and works for **any** role — not just `system` and `user`:

```php theme={null}
$prompt = Deck::get('code-reviewer');

// Render the system role.
$prompt->system(['tone' => 'professional']);

// Render the user role.
$prompt->user(['input' => $code]);

// Render custom roles.
$prompt->assistant(['context' => $history]);
$prompt->developer(['task' => 'review']);
$prompt->tool(['name' => 'search']);
```

When called without arguments, the content is returned with placeholders left intact:

```php theme={null}
$prompt->system(); // "You are a {{ $tone }} AI assistant..."
```

### The `role` method

If the role name is dynamic or stored in a variable, use the explicit `role` method:

```php theme={null}
$roleName = 'assistant';

$content = $prompt->role($roleName, ['context' => $history]);
```

This is functionally identical to the magic method approach. If the role does not exist, an empty string is returned.

### Raw content

To retrieve a role's content **without** variable interpolation, use the `raw` method:

```php theme={null}
$template = $prompt->raw('system');
// "You are a {{ $tone }} AI assistant specialized in..."
```

This is useful when you need to inspect the template, store it, or perform your own interpolation logic.

## Inspecting prompts

### Available roles

The `roles` method returns an array of all role names defined in the prompt:

```php theme={null}
$prompt->roles();
// ['system', 'user', 'assistant']
```

Roles are discovered automatically from the files in the version directory. Any file matching the configured extension (e.g. `.md`) becomes a role — the filename (without extension) is the role name.

### Checking for a role

Use the `has` method to check whether a specific role exists before attempting to render it:

```php theme={null}
if ($prompt->has('assistant')) {
    $content = $prompt->assistant(['context' => $history]);
}
```

### Metadata

Metadata comes from two files, and `metadata` returns them merged:

1. The prompt's root `metadata.json` — shared by every version (name, description, and anything else you record there).
2. The version's own `v{n}/metadata.json` — specific to that one version.

Version-level keys win when both files define the same key, so a version can override the shared description without affecting its siblings.

```php theme={null}
$prompt->metadata();
// ['description' => 'Summarises customer orders', 'variables' => ['tone', 'input'], ...]
```

Metadata is an associative array. If neither file exists, an empty array is returned.

<Note>
  The `active_version` key is never included. It records which version your application serves, which is routing state rather than metadata about the template you loaded. Read it with `Deck::active()` instead.
</Note>

### Name and version

```php theme={null}
$prompt->name();    // 'order-summary'
$prompt->version(); // 2
```

## Building messages for AI APIs

### All roles

The `toMessages` method builds a messages array compatible with OpenAI, Anthropic, and other chat-completion APIs. It renders every role with the given variables and returns them in definition order:

```php theme={null}
$messages = Deck::get('chat-agent')->toMessages([
    'tone'  => 'helpful',
    'input' => $userMessage,
]);

// [
//     ['role' => 'system',    'content' => 'You are a helpful AI assistant...'],
//     ['role' => 'user',      'content' => 'Please help me with: ...'],
//     ['role' => 'assistant', 'content' => 'Based on the context...'],
// ]
```

This array can be passed directly to any AI API client:

```php theme={null}
// OpenAI
$response = OpenAI::chat()->create([
    'model'    => 'gpt-4o',
    'messages' => $messages,
]);

// Anthropic
$response = Anthropic::messages()->create([
    'model'    => 'claude-3-sonnet',
    'messages' => $messages,
]);
```

### Filtering roles

Pass a second argument to limit which roles are included and in what order:

```php theme={null}
$messages = $prompt->toMessages(
    ['tone' => 'concise'],
    ['system', 'user']  // only include these two roles
);
```

Roles specified in the filter that don't exist in the prompt are silently skipped.

## Variable interpolation

### Syntax

Deck by PromptPHP supports two placeholder syntaxes within prompt files:

| Syntax               | Example       |
| -------------------- | ------------- |
| Spaced (recommended) | `{{ $tone }}` |
| Compact              | `{{tone}}`    |

Both are replaced when you render a role with variables:

```php theme={null}
// Template: "You are a {{ $tone }} AI assistant. Task: {{task}}"

$prompt->system(['tone' => 'friendly', 'task' => 'summarise']);
// "You are a friendly AI assistant. Task: summarise"
```

<Tip>
  Use the spaced syntax (`{{ $variable }}`) for consistency with Laravel Blade
  conventions.
</Tip>

### Supported value types

Values are cast to strings via PHP's `(string)` cast, so you can pass any scalar or stringable value:

```php theme={null}
$prompt->system([
    'name'  => 'Alice',      // string
    'count' => 42,            // int → "42"
    'score' => 3.14,          // float → "3.14"
    'price' => '$100.00',     // string with special characters
]);
```

### Missing variables

Placeholders that are not matched by the provided variables are **left intact**. This lets you render in stages or identify unfilled variables:

```php theme={null}
$prompt->system(['tone' => 'friendly']);
// "You are a friendly AI assistant. Your role is {{ $role }}."
```

## Versioning

### Directory structure

Prompts are versioned using directory-based versioning. Each version lives in its own sub-directory (`v1/`, `v2/`, etc.) inside the prompt folder:

```
resources/prompts/
└── order-summary/
    ├── metadata.json          # "active_version": 1
    ├── v1/                    # live
    │   ├── metadata.json
    │   ├── system.md
    │   └── user.md
    └── v2/                    # drafted, not serving traffic yet
        ├── metadata.json
        ├── system.md
        ├── user.md
        └── assistant.md
```

Only the active version is served. Creating a new version leaves `active_version` untouched, so you can draft in production and promote with [`prompt:activate`](/core/commands#promptactivate) when ready.

Each version directory can contain:

* Any number of role files (e.g. `system.md`, `user.md`, `assistant.md`)
* An optional `metadata.json` for version-level metadata

### Listing versions

Retrieve all versions for a prompt programmatically:

```php theme={null}
$versions = Deck::versions('order-summary');

// [
//     ['version' => 1, 'path' => '...', 'metadata' => [...]],
//     ['version' => 2, 'path' => '...', 'metadata' => [...]],
// ]
```

Versions are returned sorted in ascending order. Each entry includes the version number, the absolute path to the version directory, and any metadata from that version's `metadata.json`.

Or use the Artisan command:

```bash theme={null}
php artisan prompt:list --all
```

See [Artisan Commands — prompt:list](/core/commands#promptlist) for details.

### Activating a version

Set a specific version as the active version:

```php theme={null}
Deck::activate('order-summary', 2);
Deck::activate('order-summary', 'v2');
```

The version accepts the same formats as `Deck::get()` — `2`, `'2'`, and `'v2'` are equivalent. Anything that cannot be parsed throws `InvalidVersionException`.

**When database tracking is enabled**, this updates the `prompt_versions` table — setting `is_active = false` on all versions of that prompt, then `is_active = true` on the specified version.

**When tracking is disabled**, it writes the `active_version` key to the prompt's root `metadata.json` file.

Or use the Artisan command:

```bash theme={null}
php artisan prompt:activate order-summary 2

# or

php artisan prompt:activate order-summary v2
```

See [Artisan Commands — prompt:activate](/core/commands#promptactivate) for details.

### Version resolution order

When you call `Deck::get('name')` without a version, the active version is resolved in this priority order:

1. **Database** — If tracking is enabled, looks for a version marked `is_active = true` in the `prompt_versions` table for that prompt name.
2. **metadata.json** — Reads the `active_version` key from the prompt's root `metadata.json` file.
3. **Highest version** — Falls back to the highest version number found on disk (e.g. if `v1/` and `v3/` exist, version 3 is used).

If no versions exist at all, an `InvalidVersionException` is thrown.

## Caching

When caching is enabled, loaded prompts are stored in your configured cache store to avoid repeated filesystem reads:

```php theme={null}
// config/deck.php
'cache' => [
    'enabled' => env('DECK_CACHE_ENABLED', true),
    'store'   => env('DECK_CACHE_STORE', 'file'),
    'ttl'     => 3600, // seconds
    'prefix'  => 'deck:',
],
```

The cache key follows the pattern `{prefix}{name}.v{version}`. Prompts are cached on first load and served from cache on subsequent requests until the TTL expires.

Caching is automatically **disabled** when `APP_DEBUG=true` to ensure file changes are picked up immediately during development.

See [Configuration — Cache](/getting-started/configuration#cache) for the full reference.

## Execution tracking

When database tracking is enabled, you can log prompt executions for performance monitoring, A/B testing, and audit trails:

```php theme={null}
Deck::track('order-summary', 2, [
    'input'    => ['message' => 'Summarise order #1234'],
    'output'   => 'Your order contains...',
    'tokens'   => 150,
    'latency'  => 234.5,
    'cost'     => 0.002,
    'model'    => 'gpt-4o',
    'provider' => 'openai',
    'feedback' => ['rating' => 5],
]);
```

All fields in the data array are optional. Records are inserted into the `prompt_executions` table. If tracking is disabled, the `track` method is a safe no-op.

See [Tracking & Performance](/advanced/tracking) for comprehensive documentation on the tracking system, database schema, and Eloquent models.

<Tip>
  When using the [Laravel AI SDK integration](/integrations/ai-sdk), the
  `TrackPromptMiddleware` handles execution tracking automatically — no manual
  `track()` calls needed.
</Tip>

## Serialisation

The `PromptTemplate` class implements Laravel's `Arrayable` contract. Call `toArray()` to get a serialisable representation:

```php theme={null}
$prompt->toArray();

// [
//     'name'     => 'order-summary',
//     'version'  => 2,
//     'roles'    => ['system' => '...', 'user' => '...'],
//     'metadata' => ['description' => '...'],
// ]
```

This is useful for caching, logging, debugging, or passing prompt data to queued jobs.
