Files
wp-agentic-writer/includes/class-model-registry.php
Dwindi Ramadhana 690991c526 refactor: Cleanup git state - commit all staged changes
Major refactoring cleanup:
- Add new controller architecture (class-controller-*.php)
- Add new settings-v2 UI (views/settings-v2/)
- Add new CSS architecture (agentic-sidebar.css, tokens)
- Add esbuild build pipeline (scripts/build.js, package.json)
- Add composer dependencies (vendor/)
- Add frontend src directory (assets/js/src/index.jsx)
- Add documentation files
- Remove old/obsolete files (class-settings.php, old CSS)

This commits all pending changes from previous refactoring efforts.
2026-06-17 05:27:58 +07:00

279 lines
9.0 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 [
"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;
}
}