385 lines
10 KiB
Markdown
385 lines
10 KiB
Markdown
# Remaining Implementation Code
|
|
|
|
This document contains all code snippets that need to be implemented for Phases 1.2, 1.3, 2, and 3.
|
|
|
|
---
|
|
|
|
## Phase 1.2: Writing Mode Empty State (Frontend - sidebar.js)
|
|
|
|
**Location:** Add before the main render return statement
|
|
|
|
```javascript
|
|
// Check if Writing mode needs empty state
|
|
const shouldShowWritingEmptyState = () => {
|
|
return agentMode === 'writing' && !currentPlanRef.current;
|
|
};
|
|
|
|
// Render Writing mode empty state
|
|
const renderWritingEmptyState = () => {
|
|
return wp.element.createElement('div', { className: 'wpaw-writing-empty-state' },
|
|
wp.element.createElement('div', { className: 'wpaw-empty-state-content' },
|
|
wp.element.createElement('span', { className: 'wpaw-empty-state-icon' }, '📝'),
|
|
wp.element.createElement('h3', null, 'No Outline Yet'),
|
|
wp.element.createElement('p', null, 'Writing mode requires an outline to structure your article.'),
|
|
wp.element.createElement(Button, {
|
|
isPrimary: true,
|
|
onClick: () => setAgentMode('planning'),
|
|
className: 'wpaw-empty-state-button'
|
|
}, '📝 Create Outline First'),
|
|
wp.element.createElement('p', { className: 'wpaw-empty-state-hint' },
|
|
'Or switch to ',
|
|
wp.element.createElement('button', {
|
|
onClick: () => setAgentMode('chat'),
|
|
className: 'wpaw-link-button'
|
|
}, 'Chat mode'),
|
|
' to discuss your ideas.'
|
|
)
|
|
)
|
|
);
|
|
};
|
|
```
|
|
|
|
**Location:** In handleExecuteArticle function, add at the beginning:
|
|
|
|
```javascript
|
|
// Check if plan exists
|
|
if (!currentPlanRef.current) {
|
|
setMessages(prev => [...prev, {
|
|
role: 'system',
|
|
type: 'error',
|
|
content: 'Please create an outline first. Switch to Planning mode to get started.'
|
|
}]);
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 1.2: CSS for Empty State (sidebar.css)
|
|
|
|
```css
|
|
.wpaw-writing-empty-state {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-height: 300px;
|
|
padding: 2rem;
|
|
}
|
|
|
|
.wpaw-empty-state-content {
|
|
text-align: center;
|
|
max-width: 400px;
|
|
}
|
|
|
|
.wpaw-empty-state-icon {
|
|
font-size: 3rem;
|
|
display: block;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.wpaw-empty-state-content h3 {
|
|
margin: 0 0 0.5rem 0;
|
|
font-size: 1.5rem;
|
|
color: #1e1e1e;
|
|
}
|
|
|
|
.wpaw-empty-state-content p {
|
|
color: #666;
|
|
margin: 0.5rem 0;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.wpaw-empty-state-button {
|
|
margin: 1.5rem 0 1rem 0 !important;
|
|
}
|
|
|
|
.wpaw-empty-state-hint {
|
|
font-size: 0.9rem;
|
|
margin-top: 1rem !important;
|
|
}
|
|
|
|
.wpaw-link-button {
|
|
background: none;
|
|
border: none;
|
|
color: #2271b1;
|
|
text-decoration: underline;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
font: inherit;
|
|
}
|
|
|
|
.wpaw-link-button:hover {
|
|
color: #135e96;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 1.3: Writing Mode Notes Warning (sidebar.js)
|
|
|
|
**Location:** In the message sending logic, add check for Writing mode:
|
|
|
|
```javascript
|
|
// Add this check before sending message
|
|
if (agentMode === 'writing' && currentPlanRef.current) {
|
|
// Show info about notes in writing mode
|
|
setMessages(prev => [...prev,
|
|
{ role: 'user', content: userMessage },
|
|
{
|
|
role: 'system',
|
|
type: 'info',
|
|
content: '💡 Note: To modify the outline, switch to Planning mode. Writing mode messages are for discussion only.'
|
|
}
|
|
]);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 2.1: Summarize-Context Endpoint (class-gutenberg-sidebar.php)
|
|
|
|
**Location:** In register_routes() method, add:
|
|
|
|
```php
|
|
register_rest_route(
|
|
'wp-agentic-writer/v1',
|
|
'/summarize-context',
|
|
array(
|
|
'methods' => 'POST',
|
|
'callback' => array( $this, 'handle_summarize_context' ),
|
|
'permission_callback' => array( $this, 'check_permissions' ),
|
|
)
|
|
);
|
|
```
|
|
|
|
**Location:** Add new method:
|
|
|
|
```php
|
|
/**
|
|
* Handle context summarization request.
|
|
*
|
|
* @param WP_REST_Request $request REST request.
|
|
* @return WP_REST_Response|WP_Error Response.
|
|
*/
|
|
public function handle_summarize_context( $request ) {
|
|
$params = $request->get_json_params();
|
|
$chat_history = $params['chatHistory'] ?? array();
|
|
$post_id = $params['postId'] ?? 0;
|
|
|
|
// Short history doesn't need summarization
|
|
if ( empty( $chat_history ) || count( $chat_history ) < 4 ) {
|
|
return new WP_REST_Response(
|
|
array(
|
|
'summary' => '',
|
|
'use_full_history' => true,
|
|
'cost' => 0,
|
|
'tokens_saved' => 0,
|
|
),
|
|
200
|
|
);
|
|
}
|
|
|
|
// Build history text
|
|
$history_text = '';
|
|
foreach ( $chat_history as $msg ) {
|
|
$role = ucfirst( $msg['role'] ?? 'Unknown' );
|
|
$content = $msg['content'] ?? '';
|
|
if ( ! empty( $content ) ) {
|
|
$history_text .= "{$role}: {$content}\n\n";
|
|
}
|
|
}
|
|
|
|
// Build summarization prompt
|
|
$prompt = "Summarize this conversation into key points that capture the user's intent and requirements.
|
|
|
|
Focus on:
|
|
- Main topic
|
|
- Specific focus areas
|
|
- Rejected/excluded topics
|
|
- User preferences (tone, audience, etc.)
|
|
|
|
Keep the summary concise (max 200 words) but preserve critical context.
|
|
Write in the same language as the conversation.
|
|
|
|
Output format:
|
|
TOPIC: [main topic]
|
|
FOCUS: [what to include]
|
|
EXCLUDE: [what to avoid]
|
|
PREFERENCES: [any specific requirements]
|
|
|
|
Conversation:
|
|
{$history_text}";
|
|
|
|
// Call AI with cheap model
|
|
$provider = WP_Agentic_Writer_OpenRouter_Provider::get_instance();
|
|
$messages = array(
|
|
array(
|
|
'role' => 'user',
|
|
'content' => $prompt,
|
|
),
|
|
);
|
|
|
|
$response = $provider->chat( $messages, array(), 'summarize' );
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
return $response;
|
|
}
|
|
|
|
// Calculate tokens saved
|
|
$original_tokens = count( $chat_history ) * 500; // Rough estimate
|
|
$summary_tokens = $response['output_tokens'] ?? 100;
|
|
$tokens_saved = $original_tokens - $summary_tokens;
|
|
|
|
// Track cost
|
|
do_action(
|
|
'wp_aw_after_api_request',
|
|
$post_id,
|
|
$response['model'] ?? '',
|
|
'summarize_context',
|
|
$response['input_tokens'] ?? 0,
|
|
$response['output_tokens'] ?? 0,
|
|
$response['cost'] ?? 0
|
|
);
|
|
|
|
return new WP_REST_Response(
|
|
array(
|
|
'summary' => $response['content'] ?? '',
|
|
'use_full_history' => false,
|
|
'cost' => $response['cost'] ?? 0,
|
|
'tokens_saved' => $tokens_saved,
|
|
),
|
|
200
|
|
);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 2.2: Detect-Intent Endpoint (class-gutenberg-sidebar.php)
|
|
|
|
**Location:** In register_routes() method, add:
|
|
|
|
```php
|
|
register_rest_route(
|
|
'wp-agentic-writer/v1',
|
|
'/detect-intent',
|
|
array(
|
|
'methods' => 'POST',
|
|
'callback' => array( $this, 'handle_detect_intent' ),
|
|
'permission_callback' => array( $this, 'check_permissions' ),
|
|
)
|
|
);
|
|
```
|
|
|
|
**Location:** Add new method:
|
|
|
|
```php
|
|
/**
|
|
* Handle intent detection request.
|
|
*
|
|
* @param WP_REST_Request $request REST request.
|
|
* @return WP_REST_Response|WP_Error Response.
|
|
*/
|
|
public function handle_detect_intent( $request ) {
|
|
$params = $request->get_json_params();
|
|
$last_message = $params['lastMessage'] ?? '';
|
|
$has_plan = $params['hasPlan'] ?? false;
|
|
$current_mode = $params['currentMode'] ?? 'chat';
|
|
$post_id = $params['postId'] ?? 0;
|
|
|
|
if ( empty( $last_message ) ) {
|
|
return new WP_REST_Response(
|
|
array( 'intent' => 'continue_chat' ),
|
|
200
|
|
);
|
|
}
|
|
|
|
// Build intent detection prompt
|
|
$prompt = "Based on the user's message, determine their intent. Choose ONE:
|
|
|
|
1. \"create_outline\" - User wants to create an article outline/structure
|
|
2. \"start_writing\" - User wants to write the full article
|
|
3. \"refine_content\" - User wants to improve existing content
|
|
4. \"continue_chat\" - User wants to continue discussing/exploring
|
|
5. \"clarify\" - User is asking questions or needs clarification
|
|
|
|
Consider:
|
|
- The user's explicit request
|
|
- Whether they have an outline already (has_plan: " . ( $has_plan ? 'true' : 'false' ) . ")
|
|
- Current mode (current_mode: {$current_mode})
|
|
|
|
User's message: \"{$last_message}\"
|
|
|
|
Respond with ONLY the intent code (e.g., \"create_outline\"). No explanation.";
|
|
|
|
// Call AI with cheap model
|
|
$provider = WP_Agentic_Writer_OpenRouter_Provider::get_instance();
|
|
$messages = array(
|
|
array(
|
|
'role' => 'user',
|
|
'content' => $prompt,
|
|
),
|
|
);
|
|
|
|
$response = $provider->chat( $messages, array(), 'intent_detection' );
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
return $response;
|
|
}
|
|
|
|
// Track cost
|
|
do_action(
|
|
'wp_aw_after_api_request',
|
|
$post_id,
|
|
$response['model'] ?? '',
|
|
'detect_intent',
|
|
$response['input_tokens'] ?? 0,
|
|
$response['output_tokens'] ?? 0,
|
|
$response['cost'] ?? 0
|
|
);
|
|
|
|
// Clean up response
|
|
$intent = trim( strtolower( $response['content'] ?? 'continue_chat' ) );
|
|
$intent = str_replace( '"', '', $intent );
|
|
|
|
// Validate intent
|
|
$valid_intents = array( 'create_outline', 'start_writing', 'refine_content', 'continue_chat', 'clarify' );
|
|
if ( ! in_array( $intent, $valid_intents ) ) {
|
|
$intent = 'continue_chat';
|
|
}
|
|
|
|
return new WP_REST_Response(
|
|
array(
|
|
'intent' => $intent,
|
|
'cost' => $response['cost'] ?? 0,
|
|
),
|
|
200
|
|
);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 2.3: Update Cost Tracking (class-cost-tracker.php)
|
|
|
|
**Location:** In get_operation_label() method, update the labels array:
|
|
|
|
```php
|
|
$labels = array(
|
|
'chat' => 'Chat',
|
|
'planning' => 'Planning',
|
|
'execution' => 'Article Writing',
|
|
'refinement' => 'Block Refinement',
|
|
'meta_description' => 'Meta Description',
|
|
'keyword_suggestion' => 'Keyword Suggestion',
|
|
'web_search' => 'Web Search',
|
|
'summarize_context' => 'Context Summarization', // NEW
|
|
'detect_intent' => 'Intent Detection', // NEW
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
This document contains the core implementation code. The actual integration requires careful placement in the existing codebase structure.
|