ok($this->status()); } /** * `GET /models`: the full AI catalog for pickers and diagnostics. Loading it does not block the composer. */ public function models(): JsonResponse { return $this->ok($this->gateway->availability()->toArray()); } public function dashboard(SessionRepository $sessions, ConversationStore $store, ToolRegistry $tools, UsageRollup $usage): JsonResponse { $userId = $this->userId(); $manage = current_user_can(Access::MANAGE); $availableTools = $tools->available(new ToolContext($userId)); return $this->ok($this->status() + [ 'sessions' => [ 'total' => $sessions->count($userId), 'recent' => array_map([SessionResource::class, 'make'], $sessions->list($userId, '', 5)), ], 'usage' => [ 'tokens_today' => $store->tokensToday(), 'daily_cap' => $this->settings->int('daily_token_cap'), // Earlier days come from the usage pipeline: the site's for people who manage, otherwise one's own. 'history' => $usage->history(14, $manage ? 0 : $userId), ], 'tools' => array_map(static function ($tool) use ($tools): array { return [ 'name' => $tool->name, 'label' => $tool->label, 'risk' => $tool->risk(), 'source' => $tool->source(), 'source_type' => $tool->normalizedSourceType(), 'owner' => $tool->owner, 'domain' => $tool->domain, 'catalog_group' => $tools instanceof AbilityToolRegistry ? $tools->catalogueGroupFor($tool) : ['id' => sanitize_key($tool->source()), 'label' => $tool->owner !== '' ? $tool->owner : $tool->source()], ]; }, $availableTools), ]); } /** * @return array */ protected function status(): array { $availability = $this->availabilitySummary(); return [ 'ai' => [ 'state' => $availability->state, 'providers' => $availability->providers, ], 'abilities' => AbilityToolRegistry::supported(), 'enabled' => $this->settings->runsAllowed(), 'forced_off' => defined('MAZE_GRAPH_DISABLED') && MAZE_GRAPH_DISABLED, 'default_model' => $this->settings->string('default_model'), // The app's navigation: pages of modules that are on (decision 043), and what a new chat can start with. 'modules' => $this->navigation(), 'agents' => array_values(array_map(fn (AgentDefinition $agent): array => ['id' => $agent->id, 'name' => BuiltInText::of($agent->name, $this->agentSource($agent->id)), 'description' => BuiltInText::of($agent->description, $this->agentSource($agent->id))], array_filter( $this->agents->all(), static fn (AgentDefinition $agent): bool => $agent->mode === AgentDefinition::PRIMARY && !$agent->hidden ))), 'starters' => array_values(array_map(static fn (array $item): string => BuiltInText::of((string) $item['data']['text'], $item['source']), $this->extensions->definitions()->all('starter'))), // Server versions are for the people who manage the site. 'versions' => current_user_can(Access::MANAGE) ? [ 'plugin' => MAZE_GRAPH_VERSION, 'wordpress' => get_bloginfo('version'), 'php' => PHP_VERSION, ] : ['plugin' => MAZE_GRAPH_VERSION], ]; } /** * Pages of the modules that are on, for this person. * * @return list */ protected function navigation(): array { $manage = current_user_can(Access::MANAGE); $items = []; foreach ($this->extensions->modules()->describe() as $module) { $nav = $module['nav']; if ($module['state'] !== 'on' || $nav === null || (($nav['manage'] ?? false) === true && !$manage)) { continue; } $items[] = ['module' => $module['id'], 'page' => (string) ($nav['page'] ?? ''), 'label' => (string) ($nav['label'] ?? $module['name']), 'order' => (int) ($nav['order'] ?? 100)]; } usort($items, static fn (array $a, array $b): int => $a['order'] <=> $b['order']); return $items; } protected function availabilitySummary(): Availability { return $this->gateway instanceof ModelAvailabilitySummary ? $this->gateway->summary() : $this->gateway->availability(); } /** Where an agent was defined, so only MazeGraph's own text is translated. */ private function agentSource(string $id): string { $source = (string) ($this->extensions->definitions()->get('agent', $id)['source'] ?? ''); return $source === '' && in_array($id, array_map(static fn (AgentDefinition $agent): string => $agent->id, AgentRegistry::builtIn()), true) ? Definitions::BUILT_IN : $source; } }