0 && UsageTracker::todayTaskNeurons($task) >= $limit) { return new \WP_Error('aipcf_budget', __('This feature\'s daily limit is used up. It resumes at midnight UTC.', 'aipcf-ai-provider-for-cloudflare')); } return true; } /** * How the daily neurons are shared out: each tool's own limit, for the given settings. * The ceiling is the daily budget when it is on, otherwise the free daily allowance. * * @param array $s Plugin settings. * @return array{tools: list, limits: int, ceiling: int, guarded: bool} */ public static function allocation(array $s): array { $d = Settings::defaults(); $get = static fn(string $k) => $s[$k] ?? $d[$k] ?? 0; $tools = [ ['key' => 'backfill', 'on' => !empty($s['backfill_enabled']), 'limit' => (int) ($s['backfill_daily_neurons'] ?? 0), 'tab' => 'backfill'], ['key' => 'embed', 'on' => !empty($s['embeddings_enabled']), 'limit' => (int) ($s['embed_daily_neurons'] ?? 0), 'tab' => 'embeddings'], ['key' => 'links', 'on' => !empty($get('links_enabled')), 'limit' => (int) $get('links_daily_neurons'), 'tab' => 'content'], ['key' => 'search', 'on' => !empty($get('search_enabled')), 'limit' => (int) $get('search_daily_neurons'), 'tab' => 'content'], ['key' => 'insights', 'on' => !empty($get('insights_enabled')), 'limit' => (int) $get('insights_daily_neurons'), 'tab' => 'content'], ['key' => 'chat', 'on' => !empty($get('chat_enabled')), 'limit' => (int) $get('chat_daily_neurons'), 'tab' => 'content'], ['key' => 'agents', 'on' => !empty($get('agents_enabled')), 'limit' => (int) $get('agents_daily_neurons'), 'tab' => 'content'], ]; $limits = 0; foreach ($tools as $t) { $limits += $t['on'] ? $t['limit'] : 0; } $guarded = !empty($s['budget_enabled']); $main = (int) ($s['budget_neurons'] ?? UsageTracker::FREE_TIER); return ['tools' => $tools, 'limits' => $limits, 'ceiling' => $guarded ? ($main > 0 ? $main : UsageTracker::FREE_TIER) : UsageTracker::FREE_TIER, 'guarded' => $guarded]; } /** * One sentence on why the limits do not fit, or '' when they do. * * @param array $s */ public static function overAllocation(array $s): string { $a = self::allocation($s); if ($a['limits'] <= $a['ceiling']) { return ''; } if ($a['guarded']) { $cheapest = (string) ($s['budget_action'] ?? 'stop') === 'cheapest'; return sprintf( $cheapest /* translators: 1: sum of the tools' limits, 2: the daily budget, 3: the difference */ ? __('The tools\' daily limits add up to %1$s neurons, %3$s more than the daily budget of %2$s. If they all run to their limits, the daily budget is reached first. Then text requests switch to the cheapest model, and everything else pauses until midnight UTC. Lower the tools\' limits by %3$s in total, or raise the daily budget.', 'aipcf-ai-provider-for-cloudflare') /* translators: 1: sum of the tools' limits, 2: the daily budget, 3: the difference */ : __('The tools\' daily limits add up to %1$s neurons, %3$s more than the daily budget of %2$s. If they all run to their limits, the daily budget is reached first and every AI request, including the editor\'s, stops until midnight UTC. Lower the tools\' limits by %3$s in total, or raise the daily budget.', 'aipcf-ai-provider-for-cloudflare'), number_format_i18n($a['limits']), number_format_i18n($a['ceiling']), number_format_i18n($a['limits'] - $a['ceiling']) ); } return sprintf( /* translators: 1: sum of the tools' limits, 2: the free daily allowance in neurons, 3: the difference */ __('The tools\' daily limits add up to %1$s neurons, %3$s more than the free daily allowance of %2$s, and no daily budget is set. On a busy day the difference is billed. Switch the daily budget on, or lower the tools\' limits by %3$s in total.', 'aipcf-ai-provider-for-cloudflare'), number_format_i18n($a['limits']), number_format_i18n($a['ceiling']), number_format_i18n($a['limits'] - $a['ceiling']) ); } /** * Neurons left today for a feature, or null when it has no limit of its own. */ public static function remaining(string $task): ?float { $limit = Settings::limit($task); return $limit > 0 ? max(0.0, $limit - UsageTracker::todayTaskNeurons($task)) : null; } }