Files
wp-agentic-writer/includes/class-model-registry.php

261 lines
7.4 KiB
PHP

<?php
/**
* Model Registry
*
* Centralized source of truth for model defaults, labels,
* capabilities, and provider support across the plugin.
*
* @package WP_Agentic_Writer
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class WPAW_Model_Registry
*
* Single source of truth for model configuration.
*
* Usage:
* $defaults = WPAW_Model_Registry::get_task_defaults();
* $registry = WPAW_Model_Registry::get_registry();
*/
class WPAW_Model_Registry {
/**
* Task type constants.
*/
const TASK_CHAT = 'chat';
const TASK_CLARITY = 'clarity';
const TASK_PLANNING = 'planning';
const TASK_WRITING = 'writing';
const TASK_EXECUTION = 'execution';
const TASK_REFINEMENT = 'refinement';
const TASK_ANALYSIS = 'analysis';
const TASK_SUMMARIZE = 'summarize';
const TASK_IMAGE = 'image';
/**
* Get the complete model registry.
*
* Structure:
* 'task_type' => [
* '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;
}
}