[ * 'default' => 'model_id', * 'fallback' => 'model_id', // optional * 'label' => 'Human-readable name', * 'description' => 'What this model is used for', * 'supported_providers' => ['openrouter', 'local', 'codex'], // optional * 'capabilities' => ['chat', 'streaming', 'vision'], // optional * ] * * @since 0.2.0 * @return array Model registry. */ public static function get_registry() { return [ "chat" => [ "default" => "google/gemini-2.5-flash", "fallback" => "google/gemini-2.0-flash-exp", "label" => "Chat Model", "description" => "Discussion, research, and recommendations", "capabilities" => ["chat", "streaming", "reasoning"], ], "clarity" => [ "default" => "google/gemini-2.5-flash", "fallback" => "google/gemini-2.0-flash-exp", "label" => "Clarity Model", "description" => "Prompt analysis and quiz generation", "capabilities" => ["chat", "streaming"], ], "planning" => [ "default" => "google/gemini-2.5-flash", "fallback" => "google/gemini-2.0-flash-exp", "label" => "Planning Model", "description" => "Article outline and structure generation", "capabilities" => ["chat", "streaming"], ], "writing" => [ "default" => "anthropic/claude-3.5-haiku", "fallback" => "google/gemini-2.5-flash", "label" => "Writing Model", "description" => "Article content generation", "capabilities" => ["chat", "streaming", "long_context"], ], "execution" => [ "default" => "anthropic/claude-3.5-haiku", "fallback" => "google/gemini-2.5-flash", "label" => "Execution Model", "description" => "Article section writing (alias for writing)", "capabilities" => ["chat", "streaming"], ], "refinement" => [ "default" => "anthropic/claude-sonnet-4", "fallback" => "anthropic/claude-3.5-haiku", "label" => "Refinement Model", "description" => "Paragraph edits, rewrites, and improvements", "capabilities" => ["chat", "streaming"], ], "analysis" => [ "default" => "google/gemini-2.5-flash", "fallback" => "anthropic/claude-3.5-haiku", "label" => "Analysis Model", "description" => "Content analysis and improvement suggestions", "capabilities" => ["chat", "streaming"], ], "summarize" => [ "default" => "google/gemini-2.5-flash", "fallback" => "anthropic/claude-3.5-haiku", "label" => "Summarization Model", "description" => "Context summarization and compression", "capabilities" => ["chat"], ], "image" => [ "default" => "openai/gpt-4o", "fallback" => "openai/dall-e-3", "label" => "Image Generation Model", "description" => "Image generation for articles", "capabilities" => ["image_generation"], "supported_providers" => ["openrouter"], ], "image_models" => [ "black-forest-labs/flux-1.1-pro" => "FLUX 1.1 Pro", "black-forest-labs/flux-pro" => "FLUX Pro", "recraft-ai/recraft-v3" => "Recraft V3", ], ]; } /** * Get default model for a task type. * * @since 0.2.0 * @param string $task Task type (chat, planning, execution, etc). * @return string Default model ID. */ public static function get_default_model($task) { $registry = self::get_registry(); $task_data = $registry[$task] ?? $registry["chat"]; return $task_data["default"]; } /** * Get fallback model for a task type. * * @since 0.2.0 * @param string $task Task type. * @return string Fallback model ID. */ public static function get_fallback_model($task) { $registry = self::get_registry(); $task_data = $registry[$task] ?? $registry["chat"]; return $task_data["fallback"] ?? $task_data["default"]; } /** * Get all task defaults as key-value pairs. * * @since 0.2.0 * @return array Task => default_model pairs. */ public static function get_task_defaults() { $registry = self::get_registry(); $defaults = []; foreach ($registry as $task => $data) { $defaults[$task] = $data["default"]; } return $defaults; } /** * Get activation defaults (for plugin activation). * * This returns the format expected by the settings option. * * @since 0.2.0 * @return array Settings-compatible defaults. */ public static function get_activation_defaults() { return [ "planning_model" => self::get_default_model("planning"), "execution_model" => self::get_default_model("writing"), "image_model" => self::get_default_model("image"), ]; } /** * Validate a model ID is in the registry. * * @since 0.2.0 * @param string $model Model ID. * @return bool True if valid. */ public static function is_valid_model($model) { foreach (self::get_registry() as $task_data) { if ( $model === $task_data["default"] || $model === $task_data["fallback"] ) { return true; } } return false; } /** * Get display name for a model ID. * * Extracts a human-readable name from model IDs like * "google/gemini-2.5-flash" -> "Google Gemini 2.5 Flash" * * @since 0.2.0 * @param string $model_id Model ID. * @return string Human-readable display name. */ public static function get_model_display_name($model_id) { if (empty($model_id)) { return "Unknown Model"; } // Handle known model ID patterns $display_names = [ "google/gemini-2.5-flash" => "Google Gemini 2.5 Flash", "google/gemini-2.0-flash-exp" => "Google Gemini 2.0 Flash", "google/gemini-2.0-flash-exp:free" => "Google Gemini 2.0 Flash", "anthropic/claude-sonnet-4" => "Anthropic Claude Sonnet 4", "anthropic/claude-3.5-sonnet" => "Anthropic Claude 3.5 Sonnet", "anthropic/claude-3.5-haiku" => "Anthropic Claude 3.5 Haiku", "openai/gpt-4o" => "OpenAI GPT-4o", "openai/dall-e-3" => "OpenAI DALL-E 3", "black-forest-labs/flux-schnell" => "Black Forest Flux Schnell", ]; if (isset($display_names[$model_id])) { return $display_names[$model_id]; } // Generate from model ID: "provider/model-name" -> "Provider Model Name" $parts = explode("/", $model_id); if (count($parts) >= 2) { $provider = ucfirst(str_replace("-", " ", $parts[0])); $name = ucwords(str_replace("-", " ", $parts[1])); return trim($provider . " " . $name); } return ucwords(str_replace("-", " ", $model_id)); } /** * Get JavaScript-compatible registry for frontend. * * @since 0.2.0 * @return array JS-safe registry data. */ public static function get_frontend_data() { $registry = self::get_registry(); $result = []; foreach ($registry as $task => $data) { $result[$task] = [ "default" => $data["default"], "fallback" => $data["fallback"] ?? null, "label" => $data["label"], ]; } return $result; } }