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.
This commit is contained in:
428
includes/class-controller-refinement.php
Normal file
428
includes/class-controller-refinement.php
Normal file
@@ -0,0 +1,428 @@
|
||||
<?php
|
||||
/**
|
||||
* Refinement REST Controller
|
||||
*
|
||||
* Handles content refinement operations including block refinement and regeneration.
|
||||
*
|
||||
* @package WP_Agentic_Writer
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class WP_Agentic_Writer_Controller_Refinement
|
||||
*
|
||||
* REST controller for refinement operations.
|
||||
*
|
||||
* @since 0.3.0
|
||||
*/
|
||||
class WP_Agentic_Writer_Controller_Refinement
|
||||
{
|
||||
/**
|
||||
* Sidebar instance for dependency access.
|
||||
*
|
||||
* @var WP_Agentic_Writer_Gutenberg_Sidebar
|
||||
*/
|
||||
private $sidebar;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 0.3.0
|
||||
* @param WP_Agentic_Writer_Gutenberg_Sidebar $sidebar Sidebar instance.
|
||||
*/
|
||||
public function __construct($sidebar)
|
||||
{
|
||||
$this->sidebar = $sidebar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle block refine request.
|
||||
*
|
||||
* @since 0.1.0
|
||||
* @param WP_REST_Request $request REST request.
|
||||
* @return WP_REST_Response|WP_Error|void Response or streaming.
|
||||
*/
|
||||
public function handle_block_refine($request)
|
||||
{
|
||||
$params = $request->get_json_params();
|
||||
$block_id = $params["blockId"] ?? "";
|
||||
$block_type = $params["blockType"] ?? "";
|
||||
$block_content = $params["blockContent"] ?? "";
|
||||
$refinement_request = $params["refinementRequest"] ?? "";
|
||||
$article_context = $params["articleContext"] ?? [];
|
||||
$post_id = $params["postId"] ?? 0;
|
||||
$stream = $params["stream"] ?? false;
|
||||
$chat_history = $params["chatHistory"] ?? [];
|
||||
|
||||
if (empty($block_content) || empty($refinement_request)) {
|
||||
return new WP_Error(
|
||||
"missing_data",
|
||||
__(
|
||||
"Block content and refinement request are required.",
|
||||
"wp-agentic-writer",
|
||||
),
|
||||
["status" => 400],
|
||||
);
|
||||
}
|
||||
|
||||
// Check post permission BEFORE reading post data.
|
||||
if ($post_id > 0 && !$this->sidebar->check_post_permission($post_id)) {
|
||||
return new WP_Error(
|
||||
"forbidden",
|
||||
__(
|
||||
"You do not have permission to edit this post.",
|
||||
"wp-agentic-writer",
|
||||
),
|
||||
["status" => 403],
|
||||
);
|
||||
}
|
||||
|
||||
// Only read post config after permission check.
|
||||
$post_config = $this->sidebar->resolve_post_config_from_request(
|
||||
$params,
|
||||
$post_id,
|
||||
);
|
||||
|
||||
// If streaming is requested, use streaming response.
|
||||
if ($stream) {
|
||||
return $this->sidebar->stream_block_refine(
|
||||
$block_id,
|
||||
$block_type,
|
||||
$block_content,
|
||||
$refinement_request,
|
||||
$article_context,
|
||||
$post_id,
|
||||
$post_config,
|
||||
);
|
||||
}
|
||||
|
||||
$provider_result = WP_Agentic_Writer_Provider_Manager::get_provider_for_task(
|
||||
"refinement",
|
||||
);
|
||||
$provider = $provider_result->provider;
|
||||
|
||||
// Build context from article structure.
|
||||
$context_str = "\n\nArticle Context:\n";
|
||||
$context_str .=
|
||||
"Title: " . ($article_context["title"] ?? "Unknown") . "\n";
|
||||
|
||||
if (!empty($article_context["previousBlock"])) {
|
||||
$context_str .=
|
||||
"Previous section: " .
|
||||
$article_context["previousBlock"]["heading"] .
|
||||
"\n";
|
||||
}
|
||||
|
||||
$context_str .= "Current block type: " . $block_type . "\n";
|
||||
$context_str .= "Current content:\n" . $block_content . "\n";
|
||||
|
||||
if (!empty($article_context["nextBlock"])) {
|
||||
$context_str .=
|
||||
"Next section: " .
|
||||
$article_context["nextBlock"]["heading"] .
|
||||
"\n";
|
||||
}
|
||||
|
||||
// Add chat history context if available
|
||||
$chat_history_context = "";
|
||||
if (!empty($chat_history) && is_array($chat_history)) {
|
||||
$chat_history_context = "\n\n--- ORIGINAL CONVERSATION ---\n";
|
||||
foreach ($chat_history as $msg) {
|
||||
$role = isset($msg["role"]) ? ucfirst($msg["role"]) : "Unknown";
|
||||
$content = isset($msg["content"]) ? $msg["content"] : "";
|
||||
if (
|
||||
!empty($content) &&
|
||||
"system" !== strtolower($msg["role"] ?? "")
|
||||
) {
|
||||
$chat_history_context .= "{$role}: {$content}\n\n";
|
||||
}
|
||||
}
|
||||
$chat_history_context .= "--- END CONVERSATION ---\n";
|
||||
$chat_history_context .=
|
||||
"This shows the original discussion that led to this article.";
|
||||
}
|
||||
|
||||
// Add plan context if available
|
||||
$plan_context = "";
|
||||
$plan = get_post_meta($post_id, "_wpaw_plan", true);
|
||||
if (!empty($plan) && is_array($plan)) {
|
||||
$plan_context = "\n\nOriginal Article Outline:\n";
|
||||
if (!empty($plan["title"])) {
|
||||
$plan_context .= "Title: {$plan["title"]}\n";
|
||||
}
|
||||
if (!empty($plan["sections"]) && is_array($plan["sections"])) {
|
||||
foreach ($plan["sections"] as $section) {
|
||||
$heading = $section["heading"] ?? ($section["title"] ?? "");
|
||||
if (!empty($heading)) {
|
||||
$plan_context .= "- {$heading}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$system_prompt = "You are an expert editor helping refine a specific section of an article.
|
||||
|
||||
{$context_str}
|
||||
{$plan_context}
|
||||
{$chat_history_context}
|
||||
|
||||
USER REQUEST: {$refinement_request}
|
||||
|
||||
TASK:
|
||||
Refine the current section content considering:
|
||||
1. How it fits into the overall article flow
|
||||
2. Consistency with surrounding sections
|
||||
3. The original intent from the conversation and outline
|
||||
4. The user's specific refinement request
|
||||
5. Maintaining the original key information
|
||||
|
||||
Provide the refined content in Markdown format.
|
||||
Keep the same block type (paragraph, heading, list, etc.).";
|
||||
|
||||
$messages = [
|
||||
[
|
||||
"role" => "system",
|
||||
"content" => $system_prompt,
|
||||
],
|
||||
[
|
||||
"role" => "user",
|
||||
"content" => "Please refine this content.",
|
||||
],
|
||||
];
|
||||
|
||||
$response = $provider->chat(
|
||||
$messages,
|
||||
["temperature" => 0.7],
|
||||
"execution",
|
||||
);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
return new WP_Error(
|
||||
"refinement_error",
|
||||
$response->get_error_message(),
|
||||
["status" => 500],
|
||||
);
|
||||
}
|
||||
|
||||
// Parse refined content as Gutenberg blocks.
|
||||
$blocks = WP_Agentic_Writer_Markdown_Parser::parse(
|
||||
$response["content"],
|
||||
);
|
||||
|
||||
// MEMANTO: Remember block refinement.
|
||||
do_action(
|
||||
"wpaw_memanto_block_refined",
|
||||
$post_id,
|
||||
$block_id,
|
||||
$refinement_request,
|
||||
);
|
||||
|
||||
// Track cost (always track for debugging).
|
||||
$this->sidebar->track_ai_cost(
|
||||
$post_id,
|
||||
$response["model"] ?? "",
|
||||
"block_refinement",
|
||||
$response["input_tokens"] ?? 0,
|
||||
$response["output_tokens"] ?? 0,
|
||||
$response["cost"] ?? 0,
|
||||
$provider_result,
|
||||
"",
|
||||
"success",
|
||||
);
|
||||
|
||||
return new WP_REST_Response(
|
||||
[
|
||||
"blocks" => $blocks,
|
||||
"blockId" => $block_id,
|
||||
"cost" => $response["cost"] ?? 0,
|
||||
"provider_metadata" => $this->sidebar->build_provider_metadata(
|
||||
$provider_result,
|
||||
$response["model"] ?? "",
|
||||
),
|
||||
],
|
||||
200,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle regenerate block request.
|
||||
*
|
||||
* @since 0.1.0
|
||||
* @param WP_REST_Request $request REST request.
|
||||
* @return WP_REST_Response|WP_Error Response.
|
||||
*/
|
||||
public function handle_regenerate_block($request)
|
||||
{
|
||||
// Check rate limit.
|
||||
$rate_limit = WPAW_Rate_Limiter::check("refine");
|
||||
if (is_wp_error($rate_limit)) {
|
||||
return $rate_limit;
|
||||
}
|
||||
|
||||
$params = $request->get_json_params();
|
||||
$block_content = $params["blockContent"] ?? "";
|
||||
$context = $params["context"] ?? "";
|
||||
$post_id = $params["postId"] ?? 0;
|
||||
|
||||
if (empty($block_content)) {
|
||||
return new WP_Error(
|
||||
"no_content",
|
||||
__("Block content is required.", "wp-agentic-writer"),
|
||||
["status" => 400],
|
||||
);
|
||||
}
|
||||
|
||||
// Check post permission if post_id is provided.
|
||||
if ($post_id > 0 && !$this->sidebar->check_post_permission($post_id)) {
|
||||
return new WP_Error(
|
||||
"forbidden",
|
||||
__(
|
||||
"You do not have permission to edit this post.",
|
||||
"wp-agentic-writer",
|
||||
),
|
||||
["status" => 403],
|
||||
);
|
||||
}
|
||||
|
||||
// Get provider for writing task.
|
||||
$provider_result = WP_Agentic_Writer_Provider_Manager::get_provider_for_task(
|
||||
"writing",
|
||||
);
|
||||
$provider = $provider_result->provider;
|
||||
|
||||
$messages = [
|
||||
[
|
||||
"role" => "system",
|
||||
"content" =>
|
||||
"You are an expert technical writer. Rewrite the provided content to improve it while maintaining the same meaning and key information.",
|
||||
],
|
||||
[
|
||||
"role" => "user",
|
||||
"content" => "Context: {$context}\n\nOriginal content:\n\n{$block_content}\n\nPlease rewrite this content.",
|
||||
],
|
||||
];
|
||||
|
||||
$response = $provider->chat(
|
||||
$messages,
|
||||
["temperature" => 0.8],
|
||||
"execution",
|
||||
);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
// Track failed attempt for observability.
|
||||
$this->sidebar->track_ai_cost(
|
||||
$post_id,
|
||||
WPAW_Model_Registry::get_default_model("writing"),
|
||||
"regeneration",
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
$provider_result,
|
||||
"",
|
||||
"error",
|
||||
);
|
||||
return new WP_Error(
|
||||
"regeneration_error",
|
||||
$response->get_error_message(),
|
||||
["status" => 500],
|
||||
);
|
||||
}
|
||||
|
||||
// Track cost (always track for debugging).
|
||||
$this->sidebar->track_ai_cost(
|
||||
$post_id,
|
||||
$response["model"] ?? "",
|
||||
"regeneration",
|
||||
$response["input_tokens"] ?? 0,
|
||||
$response["output_tokens"] ?? 0,
|
||||
$response["cost"] ?? 0,
|
||||
$provider_result,
|
||||
"",
|
||||
"success",
|
||||
);
|
||||
|
||||
return new WP_REST_Response(
|
||||
[
|
||||
"content" => $response["content"],
|
||||
"cost" => $response["cost"] ?? 0,
|
||||
"provider_metadata" => $this->sidebar->build_provider_metadata(
|
||||
$provider_result,
|
||||
$response["model"] ?? "",
|
||||
),
|
||||
],
|
||||
200,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle refine from chat request.
|
||||
*
|
||||
* @since 0.1.0
|
||||
* @param WP_REST_Request $request REST request.
|
||||
* @return void Streams response to client.
|
||||
*/
|
||||
public function handle_refine_from_chat($request)
|
||||
{
|
||||
$params = $request->get_json_params();
|
||||
$message = $params["topic"] ?? "";
|
||||
$selected_block = $params["selectedBlockClientId"] ?? "";
|
||||
$post_id = $params["postId"] ?? 0;
|
||||
$session_id = $this->sidebar->resolve_or_create_session_id(
|
||||
$params["sessionId"] ?? "",
|
||||
$post_id,
|
||||
);
|
||||
$blocks_to_refine = $params["blocksToRefine"] ?? [];
|
||||
$all_blocks = $params["allBlocks"] ?? [];
|
||||
$diff_plan = !empty($params["diffPlan"]);
|
||||
$selective_refine = !empty($params["selectiveRefine"]);
|
||||
$audit_context =
|
||||
isset($params["auditContext"]) && is_array($params["auditContext"])
|
||||
? $params["auditContext"]
|
||||
: [];
|
||||
|
||||
if (empty($blocks_to_refine) || !is_array($blocks_to_refine)) {
|
||||
return new WP_Error(
|
||||
"no_blocks_mentioned",
|
||||
__(
|
||||
"No valid blocks found to refine. Try mentioning blocks like @this, @previous, or specific blocks like @paragraph-1",
|
||||
"wp-agentic-writer",
|
||||
),
|
||||
["status" => 400],
|
||||
);
|
||||
}
|
||||
|
||||
// Check post permission BEFORE reading post data.
|
||||
if ($post_id > 0 && !$this->sidebar->check_post_permission($post_id)) {
|
||||
return new WP_Error(
|
||||
"forbidden",
|
||||
__(
|
||||
"You do not have permission to edit this post.",
|
||||
"wp-agentic-writer",
|
||||
),
|
||||
["status" => 403],
|
||||
);
|
||||
}
|
||||
|
||||
// Only read post config after permission check.
|
||||
$post_config = $this->sidebar->resolve_post_config_from_request(
|
||||
$params,
|
||||
$post_id,
|
||||
);
|
||||
|
||||
// Stream refinement for each mentioned block
|
||||
$this->sidebar->stream_refinement_from_chat(
|
||||
$blocks_to_refine,
|
||||
$message,
|
||||
$selected_block,
|
||||
$post_id,
|
||||
$all_blocks,
|
||||
$diff_plan,
|
||||
$post_config,
|
||||
$session_id,
|
||||
$selective_refine,
|
||||
$audit_context,
|
||||
);
|
||||
|
||||
// Return early to avoid REST API trying to send headers after streaming
|
||||
exit();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user