29 KiB
WP Agentic Writer: Model Recommender Implementation Brief
Executive Summary
The Model Recommender is an interactive guided experience in the plugin settings that asks users 4–5 questions, then automatically generates and applies a personalized OpenRouter model configuration matching their budget and use case.
Why it exists: Users arrive with widely varying budgets (totally free → premium agencies). The 3 presets (Budget/Balanced/Premium) cover 80% of use cases, but the Recommender handles the remaining 20%: ultra-low-budget users, niche workflows, or custom combinations.
Where it lives: Settings page → "AI Model Configuration" card → Button: "Open Model Recommender"
Output: A filled-in model configuration that users can save or customize.
Table of Contents
- User Flow
- System Prompt (Agent Logic)
- Question Schema
- Response JSON Format
- Backend Implementation
- Frontend UI Component
- Conversation Examples
- Edge Cases & Fallbacks
- Development Checklist
User Flow
User clicks: [Open Model Recommender] button on settings page
↓
Modal opens with welcoming intro text
↓
Question 1: "What's your main goal?"
├─ Dev blog, agency client content, hobby writing, etc.
↓
Question 2: "What's your budget per article?"
├─ <$0.01, $0.01–0.05, $0.05–0.20, >$0.20
↓
Question 3: "How many articles per month?"
├─ 1–3, 4–8, 9–15, 15+
↓
Question 4: "Do you need AI-generated images?"
├─ Yes, No, Optional (depends on image quality)
↓
(Optional) Question 5: "Any specific provider preference?"
├─ No preference, Google Gemini, OpenAI, Anthropic
↓
Agent processes answers → generates recommended config JSON
↓
Modal displays: "Recommended config: [Name]"
├─ Chat: [Model]
├─ Writing: [Model]
├─ etc.
├─ Estimated cost: $X/article
↓
User: [Apply Config] or [Customize Manually] or [Cancel]
↓
Config saved to plugin options / UI refreshed
System Prompt (Agent Logic)
This prompt runs server-side to convert user answers into a model configuration.
System Prompt for Model Recommender Agent
═══════════════════════════════════════════════════════════════
You are a Model Configuration Recommender for WP Agentic Writer.
Your job: Convert user answers (goal, budget, volume, preferences)
into an optimal OpenRouter model preset for 6 tasks:
- chat, clarity, planning, writing, refinement, image
You MUST:
1. Respect budget constraints strictly
2. Optimize cost:quality tradeoff for use case
3. Prefer Gemini Flash for planning/chat (best value)
4. Prefer Claude Sonnet for writing (industry standard)
5. Disable images if budget <$0.01/article
6. Return ONLY valid JSON (no extra text)
Budget tiers (for reference):
- Ultra (<$0.01/article): Gemini Flash only, no images
- Budget ($0.01–0.05): DeepSeek + Flash, FLUX.2 klein images
- Balanced ($0.05–0.20): Gemini Flash + Claude Sonnet, Riverflow images
- Premium (>$0.20): GPT-5.2/Opus + Flash, FLUX.2 max images
User answers will come as:
{
"goal": "string",
"budget_per_article": "string ($0.01–0.05, etc.)",
"articles_per_month": "number",
"images_needed": "yes/no/optional",
"provider_preference": "string or null"
}
Return a JSON object with this schema:
{
"preset_name": "string (e.g., 'Budget: Gemini Flash + DeepSeek')",
"rationale": "string (brief explanation of why this config)",
"estimated_cost_per_article": number (USD, including 5.5% fee),
"models": {
"chat": "model_slug_string",
"clarity": "model_slug_string",
"planning": "model_slug_string",
"writing": "model_slug_string",
"refinement": "model_slug_string",
"image": "model_slug_string or null"
},
"recommendations": {
"when_to_use": "string",
"workflow_expectation": "string"
}
}
Examples of model slugs (valid on OpenRouter):
- deepseek-v3
- google/gemini-3-flash-preview
- anthropic/claude-3.5-sonnet
- anthropic/claude-sonnet-4
- mistral/mistral-small
- openai/gpt-5.2
- black-forest-labs/flux.2-klein
- black-forest-labs/flux.2-max
- sourceful/riverflow-v2-max
If budget <$0.01/article:
- Recommend: Gemini Flash for ALL tasks
- Image: null (disabled)
- Estimated cost: ~$0.008–0.012/article
If budget $0.01–0.05/article:
- Chat/Planning: Gemini Flash
- Writing/Refinement: Mistral Small or DeepSeek
- Images: FLUX.2 klein or null
- Estimated cost: $0.03–0.05/article
If budget $0.05–0.20/article:
- Chat/Clarity/Planning: Gemini Flash
- Writing/Refinement: Claude Sonnet
- Images: Riverflow V2 Max or FLUX.2 Pro
- Estimated cost: $0.10–0.18/article
If budget >$0.20/article:
- Chat/Planning: Gemini Flash (cost doesn't improve)
- Clarity: Claude Sonnet 4 (nuanced feedback)
- Writing/Refinement: GPT-5.2 or Claude Opus
- Images: FLUX.2 max
- Estimated cost: $0.25–0.50+/article
Return valid JSON only. No markdown, no explanations outside the JSON.
Question Schema
Frontend: Questions Array (React/JS)
const recommenderQuestions = [
{
id: "goal",
question: "What's your main goal for this plugin?",
type: "radio",
options: [
{ value: "dev_blog", label: "Dev blog / technical tutorials" },
{ value: "agency_content", label: "Agency client content" },
{ value: "hobby_writing", label: "Hobby writing / personal blog" },
{ value: "marketing", label: "Marketing / sales content" },
{ value: "research", label: "Research papers / long-form" }
],
required: true
},
{
id: "budget_per_article",
question: "What's your budget limit per article?",
type: "radio",
options: [
{ value: "<0.01", label: "Ultra-budget (< $0.01 / article)" },
{ value: "0.01-0.05", label: "Budget ($0.01–$0.05 / article)" },
{ value: "0.05-0.20", label: "Balanced ($0.05–$0.20 / article)" },
{ value: ">0.20", label: "Premium (> $0.20 / article)" },
{ value: "no_limit", label: "No budget limit" }
],
required: true
},
{
id: "articles_per_month",
question: "How many articles per month do you plan to generate?",
type: "radio",
options: [
{ value: "1-3", label: "1–3 articles/month" },
{ value: "4-8", label: "4–8 articles/month" },
{ value: "9-15", label: "9–15 articles/month" },
{ value: "15+", label: "15+ articles/month" }
],
required: true
},
{
id: "images_needed",
question: "Do you need AI-generated images?",
type: "radio",
options: [
{ value: "yes", label: "Yes, high-quality hero images" },
{ value: "optional", label: "Optional / nice-to-have" },
{ value: "no", label: "No, I'll upload my own" }
],
required: true
},
{
id: "provider_preference",
question: "(Optional) Any provider preference?",
type: "radio",
options: [
{ value: null, label: "No preference (use what's best)" },
{ value: "google", label: "Google (Gemini)" },
{ value: "openai", label: "OpenAI (GPT)" },
{ value: "anthropic", label: "Anthropic (Claude)" }
],
required: false
}
];
Response JSON Format
Backend: Agent Response
{
"preset_name": "Balanced: Gemini Flash + Claude Sonnet + Riverflow",
"rationale": "Your budget ($0.10–0.20/article) and moderate volume (8 articles/month) fit the Balanced preset perfectly. Gemini Flash handles chat/planning efficiently; Claude Sonnet is the industry standard for long-form writing. Riverflow images provide high quality at flat $0.03/image.",
"estimated_cost_per_article": 0.1359,
"models": {
"chat": "google/gemini-3-flash-preview",
"clarity": "google/gemini-3-flash-preview",
"planning": "google/gemini-3-flash-preview",
"writing": "anthropic/claude-3.5-sonnet",
"refinement": "anthropic/claude-3.5-sonnet",
"image": "sourceful/riverflow-v2-max"
},
"recommendations": {
"when_to_use": "Regular blogging (4–10 articles/month), mixed content types, publishing to professional blog or portfolio.",
"workflow_expectation": "User does one refinement cycle; publishes with high confidence."
}
}
Backend Implementation
1. PHP Endpoint (WordPress REST API)
<?php
// File: includes/class-model-recommender.php
class Agentic_Writer_Model_Recommender {
/**
* Register REST endpoint
*/
public static function register_endpoints() {
register_rest_route(
'agentic-writer/v1',
'/recommend-models',
[
'methods' => 'POST',
'callback' => [ self::class, 'get_recommendation' ],
'permission_callback' => [ self::class, 'check_permissions' ],
'args' => [
'goal' => ['required' => true],
'budget_per_article' => ['required' => true],
'articles_per_month' => ['required' => true],
'images_needed' => ['required' => true],
'provider_preference' => ['required' => false],
]
]
);
}
/**
* Get model recommendation from Claude/LLM
*/
public static function get_recommendation( $request ) {
$params = $request->get_json_params();
// Build prompt with user answers
$user_input = self::format_user_input( $params );
// Call OpenRouter API (using Claude 3.5 Sonnet for reasoning)
$recommendation = self::call_recommender_agent( $user_input );
if ( is_wp_error( $recommendation ) ) {
return new WP_REST_Response(
['error' => $recommendation->get_error_message()],
500
);
}
return new WP_REST_Response( $recommendation, 200 );
}
/**
* Format user answers into structured prompt
*/
private static function format_user_input( $params ) {
return json_encode([
'goal' => $params['goal'] ?? null,
'budget_per_article' => $params['budget_per_article'] ?? null,
'articles_per_month' => (int) $params['articles_per_month'] ?? null,
'images_needed' => $params['images_needed'] ?? 'no',
'provider_preference' => $params['provider_preference'] ?? null,
]);
}
/**
* Call OpenRouter API with system prompt + user input
*/
private static function call_recommender_agent( $user_input ) {
$api_key = get_option( 'agentic_writer_openrouter_api_key' );
if ( !$api_key ) {
return new WP_Error(
'no_api_key',
'OpenRouter API key not configured.'
);
}
$system_prompt = self::get_system_prompt();
$response = wp_remote_post(
'https://openrouter.ai/api/v1/chat/completions',
[
'headers' => [
'Authorization' => 'Bearer ' . $api_key,
'Content-Type' => 'application/json',
],
'body' => json_encode([
'model' => 'anthropic/claude-3.5-sonnet',
'messages' => [
[
'role' => 'system',
'content' => $system_prompt,
],
[
'role' => 'user',
'content' => "User answers: {$user_input}\n\nBased on these answers, generate a recommended model configuration.",
]
],
'temperature' => 0.2, // Deterministic
'max_tokens' => 500,
]),
]
);
if ( is_wp_error( $response ) ) {
return $response;
}
$body = json_decode( wp_remote_retrieve_body( $response ), true );
if ( !isset( $body['choices'][0]['message']['content'] ) ) {
return new WP_Error(
'invalid_response',
'Invalid response from OpenRouter API.'
);
}
$content = $body['choices'][0]['message']['content'];
// Parse JSON from response
$recommendation = json_decode( $content, true );
if ( !$recommendation ) {
return new WP_Error(
'invalid_json',
'Could not parse agent recommendation.'
);
}
return $recommendation;
}
/**
* Get system prompt (embedded in class)
*/
private static function get_system_prompt() {
return <<<'PROMPT'
You are a Model Configuration Recommender for WP Agentic Writer.
Your job: Convert user answers (goal, budget, volume, preferences)
into an optimal OpenRouter model preset for 6 tasks:
- chat, clarity, planning, writing, refinement, image
You MUST:
1. Respect budget constraints strictly
2. Optimize cost:quality tradeoff for use case
3. Prefer Gemini Flash for planning/chat (best value)
4. Prefer Claude Sonnet for writing (industry standard)
5. Disable images if budget <$0.01/article
6. Return ONLY valid JSON (no extra text)
Budget tiers (for reference):
- Ultra (<$0.01/article): Gemini Flash only, no images
- Budget ($0.01–0.05): DeepSeek + Flash, FLUX.2 klein images
- Balanced ($0.05–0.20): Gemini Flash + Claude Sonnet, Riverflow images
- Premium (>$0.20): GPT-5.2/Opus + Flash, FLUX.2 max images
Valid model slugs (OpenRouter):
- deepseek-v3
- google/gemini-3-flash-preview
- anthropic/claude-3.5-sonnet
- anthropic/claude-sonnet-4
- mistral/mistral-small
- openai/gpt-5.2
- black-forest-labs/flux.2-klein
- black-forest-labs/flux.2-max
- sourceful/riverflow-v2-max
Return ONLY this JSON structure (no markdown, no extra text):
{
"preset_name": "string",
"rationale": "string",
"estimated_cost_per_article": number,
"models": {
"chat": "string",
"clarity": "string",
"planning": "string",
"writing": "string",
"refinement": "string",
"image": "string or null"
},
"recommendations": {
"when_to_use": "string",
"workflow_expectation": "string"
}
}
PROMPT;
}
/**
* Check user permissions
*/
public static function check_permissions() {
return current_user_can( 'manage_options' );
}
}
// Hook to register on init
add_action( 'rest_api_init', [ 'Agentic_Writer_Model_Recommender', 'register_endpoints' ] );
Frontend UI Component
React Component (Gutenberg/Admin UI)
// File: components/ModelRecommender.jsx
import React, { useState } from 'react';
import { Button, Modal, RadioControl, Spinner } from '@wordpress/components';
import apiFetch from '@wordpress/api-fetch';
export const ModelRecommender = ({ onApplyConfig }) => {
const [isOpen, setIsOpen] = useState(false);
const [currentStep, setCurrentStep] = useState(0);
const [answers, setAnswers] = useState({
goal: null,
budget_per_article: null,
articles_per_month: null,
images_needed: 'no',
provider_preference: null,
});
const [loading, setLoading] = useState(false);
const [recommendation, setRecommendation] = useState(null);
const questions = [
{
id: 'goal',
question: 'What\'s your main goal for this plugin?',
options: [
{ value: 'dev_blog', label: 'Dev blog / technical tutorials' },
{ value: 'agency_content', label: 'Agency client content' },
{ value: 'hobby_writing', label: 'Hobby writing / personal blog' },
{ value: 'marketing', label: 'Marketing / sales content' },
{ value: 'research', label: 'Research papers / long-form' },
],
},
{
id: 'budget_per_article',
question: 'What\'s your budget limit per article?',
options: [
{ value: '<0.01', label: 'Ultra-budget (< $0.01)' },
{ value: '0.01-0.05', label: 'Budget ($0.01–$0.05)' },
{ value: '0.05-0.20', label: 'Balanced ($0.05–$0.20)' },
{ value: '>0.20', label: 'Premium (> $0.20)' },
{ value: 'no_limit', label: 'No budget limit' },
],
},
{
id: 'articles_per_month',
question: 'How many articles per month?',
options: [
{ value: '1-3', label: '1–3 articles/month' },
{ value: '4-8', label: '4–8 articles/month' },
{ value: '9-15', label: '9–15 articles/month' },
{ value: '15+', label: '15+ articles/month' },
],
},
{
id: 'images_needed',
question: 'Do you need AI-generated images?',
options: [
{ value: 'yes', label: 'Yes, high-quality hero images' },
{ value: 'optional', label: 'Optional / nice-to-have' },
{ value: 'no', label: 'No, I\'ll upload my own' },
],
},
{
id: 'provider_preference',
question: '(Optional) Any provider preference?',
options: [
{ value: null, label: 'No preference' },
{ value: 'google', label: 'Google Gemini' },
{ value: 'openai', label: 'OpenAI GPT' },
{ value: 'anthropic', label: 'Anthropic Claude' },
],
},
];
const handleAnswerChange = (answerId, value) => {
setAnswers(prev => ({ ...prev, [answerId]: value }));
};
const handleNext = async () => {
if (currentStep < questions.length - 1) {
setCurrentStep(currentStep + 1);
} else {
// Submit to backend
await submitRecommendation();
}
};
const submitRecommendation = async () => {
setLoading(true);
try {
const rec = await apiFetch({
path: '/agentic-writer/v1/recommend-models',
method: 'POST',
data: answers,
});
setRecommendation(rec);
} catch (error) {
alert('Error generating recommendation: ' + error.message);
} finally {
setLoading(false);
}
};
const handleApply = () => {
onApplyConfig(recommendation);
setIsOpen(false);
setCurrentStep(0);
setRecommendation(null);
};
const currentQuestion = questions[currentStep];
const isAnswered = answers[currentQuestion.id] !== null;
return (
<>
<Button
variant="primary"
onClick={() => setIsOpen(true)}
style={{ marginTop: '10px' }}
>
Open Model Recommender
</Button>
{isOpen && (
<Modal
title="AI Model Recommender"
onRequestClose={() => setIsOpen(false)}
style={{ width: '500px' }}
>
{recommendation ? (
// Results view
<div style={{ padding: '20px' }}>
<h3>Recommended Configuration</h3>
<p><strong>{recommendation.preset_name}</strong></p>
<p><em>{recommendation.rationale}</em></p>
<table style={{ width: '100%', marginTop: '20px', borderCollapse: 'collapse' }}>
<tbody>
<tr>
<td><strong>Chat</strong></td>
<td>{recommendation.models.chat}</td>
</tr>
<tr>
<td><strong>Clarity</strong></td>
<td>{recommendation.models.clarity}</td>
</tr>
<tr>
<td><strong>Planning</strong></td>
<td>{recommendation.models.planning}</td>
</tr>
<tr>
<td><strong>Writing</strong></td>
<td>{recommendation.models.writing}</td>
</tr>
<tr>
<td><strong>Refinement</strong></td>
<td>{recommendation.models.refinement}</td>
</tr>
<tr>
<td><strong>Image</strong></td>
<td>{recommendation.models.image || 'Disabled'}</td>
</tr>
</tbody>
</table>
<p style={{ marginTop: '20px' }}>
<strong>Estimated cost:</strong> ${recommendation.estimated_cost_per_article.toFixed(4)}/article
</p>
<div style={{ marginTop: '20px', display: 'flex', gap: '10px' }}>
<Button
variant="primary"
onClick={handleApply}
>
Apply This Configuration
</Button>
<Button
variant="secondary"
onClick={() => {
setRecommendation(null);
setCurrentStep(0);
}}
>
Go Back
</Button>
</div>
</div>
) : (
// Questions view
<div style={{ padding: '20px' }}>
<p><strong>Question {currentStep + 1} of {questions.length}</strong></p>
<h3>{currentQuestion.question}</h3>
<RadioControl
selected={answers[currentQuestion.id]}
options={currentQuestion.options}
onChange={(value) => handleAnswerChange(currentQuestion.id, value)}
/>
<div style={{ marginTop: '20px', display: 'flex', gap: '10px' }}>
<Button
variant="primary"
onClick={handleNext}
disabled={!isAnswered || loading}
>
{loading ? <Spinner /> : (currentStep === questions.length - 1 ? 'Get Recommendation' : 'Next')}
</Button>
{currentStep > 0 && (
<Button
variant="secondary"
onClick={() => setCurrentStep(currentStep - 1)}
>
Back
</Button>
)}
</div>
</div>
)}
</Modal>
)}
</>
);
};
Conversation Examples
Example 1: Ultra-Budget Dev Blogger
User answers:
- Goal: Dev blog
- Budget: <$0.01/article
- Volume: 2 articles/month
- Images: No
- Provider: No preference
Agent response:
{
"preset_name": "Ultra-Budget: Gemini Flash Only",
"rationale": "Your ultra-low budget and dev blog focus make Gemini 3 Flash the perfect fit. It's optimized for coding + reasoning at nearly free cost. No images keeps cost minimal.",
"estimated_cost_per_article": 0.0082,
"models": {
"chat": "google/gemini-3-flash-preview",
"clarity": "google/gemini-3-flash-preview",
"planning": "google/gemini-3-flash-preview",
"writing": "google/gemini-3-flash-preview",
"refinement": "google/gemini-3-flash-preview",
"image": null
},
"recommendations": {
"when_to_use": "Solo dev bloggers with minimal budget; testing the plugin before investing.",
"workflow_expectation": "Output may need 1–2 editing rounds before publishing."
}
}
Example 2: Agency with Quality Focus
User answers:
- Goal: Agency client content
- Budget: >$0.20/article
- Volume: 20 articles/month
- Images: Yes, high-quality
- Provider: No preference
Agent response:
{
"preset_name": "Premium: GPT-5.2 + Gemini Flash + FLUX.2 max",
"rationale": "Your agency volume and quality-first mindset justify premium models. GPT-5.2 delivers publication-ready prose; FLUX.2 max generates hero images. Your clients will notice the difference.",
"estimated_cost_per_article": 0.3142,
"models": {
"chat": "google/gemini-3-flash-preview",
"clarity": "anthropic/claude-sonnet-4",
"planning": "google/gemini-3-flash-preview",
"writing": "openai/gpt-5.2",
"refinement": "openai/gpt-5.2",
"image": "black-forest-labs/flux.2-max"
},
"recommendations": {
"when_to_use": "Agencies publishing 15+ articles/month; thought leadership, flagship posts, client deliverables.",
"workflow_expectation": "Minimal editing required; publish with high confidence."
}
}
Example 3: Balanced Hobbyist
User answers:
- Goal: Hobby writing / personal blog
- Budget: $0.05–$0.20/article
- Volume: 4 articles/month
- Images: Optional
- Provider: Google Gemini
Agent response:
{
"preset_name": "Balanced: Gemini Flash + Claude Sonnet + Riverflow",
"rationale": "Your modest volume and provider preference align perfectly with the Balanced preset. All-Gemini planning + Claude writing = great quality at fair cost. Riverflow images optional but affordable.",
"estimated_cost_per_article": 0.1359,
"models": {
"chat": "google/gemini-3-flash-preview",
"clarity": "google/gemini-3-flash-preview",
"planning": "google/gemini-3-flash-preview",
"writing": "anthropic/claude-3.5-sonnet",
"refinement": "anthropic/claude-3.5-sonnet",
"image": "sourceful/riverflow-v2-max"
},
"recommendations": {
"when_to_use": "Regular hobby blogging or personal portfolio; professional output without premium cost.",
"workflow_expectation": "One refinement cycle typical; polished final product."
}
}
Edge Cases & Fallbacks
1. API failure (OpenRouter down)
Fallback behavior:
// If recommender API fails, load from static presets
if ( is_wp_error( $recommendation ) ) {
$recommendation = self::get_fallback_preset_by_budget(
$params['budget_per_article']
);
}
2. Budget = "no_limit"
Logic:
- Treat as ">$0.20" (Premium tier).
- Recommend frontier models + best images.
3. Invalid JSON from agent
Fallback:
- Log error to error_log.
- Show message: "Recommender had trouble generating a config. Try adjusting your answers."
- Offer manual preset picker instead.
4. User cancels mid-flow
Behavior:
- Modal closes cleanly.
- No settings changed.
- State resets for next usage.
Development Checklist
-
Backend setup
- Create
includes/class-model-recommender.php - Register REST endpoint
/agentic-writer/v1/recommend-models - Implement
call_recommender_agent()with OpenRouter API - Add error handling + fallbacks
- Test with real OpenRouter key
- Create
-
System prompt
- Write and refine system prompt (see above)
- Test with 3–5 example user inputs
- Verify JSON output is parseable
- Ensure cost estimates match model-presets.md
-
Frontend component
- Create React component:
ModelRecommender.jsx - Implement question flow (5 questions)
- Add results display with config summary
- Wire up "Apply Configuration" button
- Test modal UX (open/close/back/next)
- Create React component:
-
Integration
- Add button to settings page → "AI Model Configuration" card
- Wire up
onApplyConfigcallback to save settings - Refresh model selectors after apply
- Show success toast message
-
Testing
- Test all 5 questions with valid OpenRouter API key
- Test budget boundary cases ($0.01, $0.05, $0.20)
- Test edge cases (ultra-budget, no-limit, specific providers)
- Test API failure + fallback behavior
- Test invalid JSON response handling
- Test user cancellation mid-flow
-
Documentation
- Add to settings help: "Click 'Open Model Recommender' for personalized setup"
- Document system prompt in code comments
- Add troubleshooting guide: "Recommender not working? Check API key."
Security Considerations
-
API Key Protection:
- Only server-side calls to OpenRouter (key never exposed to client)
- REST endpoint requires
manage_optionscapability
-
Rate Limiting:
- Add WordPress nonce to prevent CSRF
- Limit calls to 1 per minute per user
-
Input Validation:
- Validate all user answers against whitelist
- Reject invalid budget ranges
// Example: Add nonce validation
register_rest_route(
'agentic-writer/v1',
'/recommend-models',
[
'callback' => [ self::class, 'get_recommendation' ],
'permission_callback' => function () {
return current_user_can( 'manage_options' )
&& isset( $_REQUEST['_wpnonce'] )
&& wp_verify_nonce( $_REQUEST['_wpnonce'], 'agentic_recommender' );
}
]
);
Cost Estimation for Development
| Task | Time | Notes |
|---|---|---|
| Backend endpoint + system prompt | 3–4 hours | Includes testing with OpenRouter |
| React component + UI | 3–4 hours | Includes modal, form flow, validation |
| Integration with settings page | 1–2 hours | Wire up button, callbacks, save logic |
| Testing + refinement | 2–3 hours | Edge cases, error handling |
| Total | 9–13 hours | Can be split across team |
Next Steps (Priority Order)
- Refine system prompt – Test with Claude to ensure it generates correct JSON
- Build backend endpoint – Implement
/recommend-modelsroute with error handling - Build React component – Create modal UI with question flow
- Integration – Wire up to settings page
- Testing – Full QA with real OpenRouter API
Document version: 1.0
Date: January 22, 2026
Author: WP Agentic Writer Product Team
Status: Ready for Development