$maxAnswerTokens the maximum answer length of every assistant (empty: the default is assumed) * @return int the largest maximum answer length, at least 1 */ public static function largestAnswer(array $maxAnswerTokens): int { $max = $maxAnswerTokens === [] ? self::DEFAULT_MAX_ANSWER : max($maxAnswerTokens); return max(1, $max); } /** The least one answer can ever reserve, with any adapter. Below this a conversation cannot be answered. */ public static function floorUnits(int $maxAnswer): int { return $maxAnswer + self::MIN_INPUT; } /** The least one answer of OpenAI, Anthropic or Gemini reserves before counting the text sent (the input bytes come on top). */ public static function providerFloorUnits(int $maxAnswer): int { return $maxAnswer + self::REAL_HEADROOM; } /** * @param mixed $raw the submitted value * @param list $maxAnswerTokens the maximum answer length of every assistant * @return array{status:string, value:int, floor:int, provider_floor:int, max_answer:int} * value is the number to save when status is not REFUSED or INVALID, otherwise 0 */ public static function assess(mixed $raw, array $maxAnswerTokens): array { $maxAnswer = self::largestAnswer($maxAnswerTokens); $floor = self::floorUnits($maxAnswer); $providerFloor = self::providerFloorUnits($maxAnswer); $base = ['floor' => $floor, 'provider_floor' => $providerFloor, 'max_answer' => $maxAnswer]; $value = self::wholeNumber($raw); if ($value === null) { return ['status' => self::INVALID, 'value' => 0] + $base; } if ($value < $floor) { return ['status' => self::REFUSED, 'value' => 0] + $base; } $status = match (true) { $value < $providerFloor => self::TOO_LOW_FOR_PROVIDER, $value < 2 * $providerFloor => self::TIGHT, default => self::OK, }; return ['status' => $status, 'value' => $value] + $base; } /** The status of a value that is already saved (for warnings on screens); never refuses, never changes it. */ public static function standing(int $saved, array $maxAnswerTokens): string { $maxAnswer = self::largestAnswer($maxAnswerTokens); return match (true) { $saved < self::floorUnits($maxAnswer) => self::REFUSED, $saved < self::providerFloorUnits($maxAnswer) => self::TOO_LOW_FOR_PROVIDER, $saved < 2 * self::providerFloorUnits($maxAnswer) => self::TIGHT, default => self::OK, }; } /** A whole number of 1 or more from a form field or a JSON integer; anything else is null. */ private static function wholeNumber(mixed $raw): ?int { if (is_int($raw)) { return $raw >= 1 ? $raw : null; } if (is_string($raw) && preg_match('/^\s*[1-9][0-9]{0,8}\s*$/', $raw) === 1) { return (int) trim($raw); } return null; } }