[ * '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 array( 'chat' => array( 'default' => 'google/gemini-2.5-flash', 'fallback' => 'google/gemini-2.0-flash-exp', 'label' => 'Chat Model', 'description' => 'Discussion, research, and recommendations', 'capabilities' => array( 'chat', 'streaming', 'reasoning' ), ), 'clarity' => array( 'default' => 'google/gemini-2.5-flash', 'fallback' => 'google/gemini-2.0-flash-exp', 'label' => 'Clarity Model', 'description' => 'Prompt analysis and quiz generation', 'capabilities' => array( 'chat', 'streaming' ), ), 'planning' => array( 'default' => 'google/gemini-2.5-flash', 'fallback' => 'google/gemini-2.0-flash-exp', 'label' => 'Planning Model', 'description' => 'Article outline and structure generation', 'capabilities' => array( 'chat', 'streaming' ), ), 'writing' => array( 'default' => 'anthropic/claude-3.5-haiku', 'fallback' => 'google/gemini-2.5-flash', 'label' => 'Writing Model', 'description' => 'Article content generation', 'capabilities' => array( 'chat', 'streaming', 'long_context' ), ), 'execution' => array( 'default' => 'anthropic/claude-3.5-haiku', 'fallback' => 'google/gemini-2.5-flash', 'label' => 'Execution Model', 'description' => 'Article section writing (alias for writing)', 'capabilities' => array( 'chat', 'streaming' ), ), 'refinement' => array( 'default' => 'anthropic/claude-3.5-sonnet', 'fallback' => 'anthropic/claude-3.5-haiku', 'label' => 'Refinement Model', 'description' => 'Paragraph edits, rewrites, and improvements', 'capabilities' => array( 'chat', 'streaming' ), ), 'analysis' => array( 'default' => 'google/gemini-2.5-flash', 'fallback' => 'anthropic/claude-3.5-haiku', 'label' => 'Analysis Model', 'description' => 'Content analysis and improvement suggestions', 'capabilities' => array( 'chat', 'streaming' ), ), 'summarize' => array( 'default' => 'google/gemini-2.5-flash', 'fallback' => 'anthropic/claude-3.5-haiku', 'label' => 'Summarization Model', 'description' => 'Context summarization and compression', 'capabilities' => array( 'chat' ), ), 'image' => array( 'default' => 'openai/gpt-4o', 'fallback' => 'openai/dall-e-3', 'label' => 'Image Generation Model', 'description' => 'Image generation for articles', 'capabilities' => array( 'image_generation' ), 'supported_providers' => array( 'openrouter' ), ), ); } /** * 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 = array(); 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 array( '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 = array( '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-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 = array(); foreach ( $registry as $task => $data ) { $result[ $task ] = array( 'default' => $data['default'], 'fallback' => $data['fallback'] ?? null, 'label' => $data['label'], ); } return $result; } }