ok($this->payload()); } public function update(Request $request, ModelGateway $gateway, Extensions $extensions): JsonResponse { $values = array_intersect_key($request->all(), Settings::DEFAULTS); $errors = []; if (isset($values['default_model'])) { $values['default_model'] = sanitize_text_field((string) $values['default_model']); $modelChanged = $values['default_model'] !== $this->settings->string('default_model'); if ($modelChanged && $values['default_model'] !== '' && !$gateway->availability()->hasModel($values['default_model'])) { $errors['default_model'][] = __('That model is not available on this site.', 'maze-graph'); } } if (isset($values['site_instructions'])) { $values['site_instructions'] = Redactor::cleanText((string) $values['site_instructions']); if (mb_strlen($values['site_instructions']) > Settings::INSTRUCTIONS_LIMIT) { $errors['site_instructions'][] = sprintf( /* translators: %s: maximum number of characters. */ __('Keep the instructions under %s characters.', 'maze-graph'), number_format_i18n(Settings::INSTRUCTIONS_LIMIT) ); } } foreach (['max_steps' => [1, 20], 'max_output_tokens' => [0, 200000], 'daily_token_cap' => [0, 1000000000], 'retention_days' => [0, 3650], 'log_retention_days' => [7, 365], 'runs_per_hour' => [0, 10000]] as $key => [$min, $max]) { if (array_key_exists($key, $values) && (!is_numeric($values[$key]) || (int) $values[$key] < $min || (int) $values[$key] > $max)) { /* translators: 1: lowest value, 2: highest value. */ $errors[$key][] = sprintf(__('Use a number from %1$s to %2$s.', 'maze-graph'), number_format_i18n($min), number_format_i18n($max)); } } if (isset($values['allowed_roles'])) { $roles = is_array($values['allowed_roles']) ? array_map('strval', $values['allowed_roles']) : []; if (!is_array($values['allowed_roles']) || array_diff($roles, array_column(Access::roles(), 'slug')) !== []) { $errors['allowed_roles'][] = __('One of those roles does not exist on this site.', 'maze-graph'); } $values['allowed_roles'] = array_values(array_unique(['administrator', ...$roles])); } if (isset($values['context_strategy']) && !in_array($values['context_strategy'], ContextBuilder::STRATEGIES, true)) { $errors['context_strategy'][] = __('Choose one of the history options.', 'maze-graph'); } if (isset($values['modules'])) { if (!is_array($values['modules'])) { $errors['modules'][] = __('Only modules that may be switched off can be changed.', 'maze-graph'); } elseif ($values['modules'] !== $this->settings->get('modules')) { $optional = array_column(array_filter($extensions->modules()->describe(), static fn (array $m): bool => $m['optional']), 'id'); if (array_diff(array_keys($values['modules']), $optional) !== []) { $errors['modules'][] = __('Only modules that may be switched off can be changed.', 'maze-graph'); } } } if (isset($values['tool_overrides']) && !is_array($values['tool_overrides'])) { $errors['tool_overrides'][] = __('Send the tool switches as names with on or off.', 'maze-graph'); } if ($errors !== []) { return $this->validationError($errors); } $this->settings->update($values); if ($values !== []) { $fields = []; foreach ($values as $key => $value) { $key = sanitize_key((string) $key); if (in_array($key, ['modules', 'tool_overrides'], true) && is_array($value)) { foreach (array_keys($value) as $name) { $fields[] = $key . ':' . sanitize_text_field((string) $name); } } else { $fields[] = $key; } } $this->events->record(new Event('settings.updated', ['user' => $this->userId(), 'fields' => $fields])); } return $this->ok($this->payload()); } /** Permanently clear the event log; Settings routes already require the manager capability. */ public function clearLogs(): JsonResponse { return $this->ok(['deleted' => $this->logs->clear()]); } /** * @return array */ protected function payload(): array { return $this->settings->all() + ['roles' => Access::roles()]; } }