$messages user/assistant turns only * @param list $context numbered reference * material for grounded answers. Adapters must present it as data, separate from the * instruction, never as instructions. */ public function __construct( public readonly string $model, public readonly string $instruction, public readonly array $messages, public readonly int $maxOutputTokens, public readonly string $requestId, public readonly int $timeoutSeconds = 60, public readonly array $context = [], ) { foreach ($messages as $m) { if (!in_array($m['role'] ?? '', ['user', 'assistant'], true)) { throw new \InvalidArgumentException('Messages may only have user or assistant roles.'); } } } /** Rough, deliberately conservative token estimate (~3 chars/token) used for reservations. */ public function estimatedInputTokens(): int { $chars = mb_strlen($this->instruction); foreach ($this->context as $c) { $chars += mb_strlen($c['title'] . $c['heading'] . $c['text']) + 16; } foreach ($this->messages as $m) { $chars += mb_strlen($m['content']) + 8; } return (int) ceil($chars / 3) + 16; } }