# 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 1. [User Flow](#user-flow) 2. [System Prompt (Agent Logic)](#system-prompt-agent-logic) 3. [Question Schema](#question-schema) 4. [Response JSON Format](#response-json-format) 5. [Backend Implementation](#backend-implementation) 6. [Frontend UI Component](#frontend-ui-component) 7. [Conversation Examples](#conversation-examples) 8. [Edge Cases & Fallbacks](#edge-cases--fallbacks) 9. [Development Checklist](#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) ```javascript 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 ```json { "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 '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) ```jsx // 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 ( <> {isOpen && ( setIsOpen(false)} style={{ width: '500px' }} > {recommendation ? ( // Results view

Recommended Configuration

{recommendation.preset_name}

{recommendation.rationale}

Chat {recommendation.models.chat}
Clarity {recommendation.models.clarity}
Planning {recommendation.models.planning}
Writing {recommendation.models.writing}
Refinement {recommendation.models.refinement}
Image {recommendation.models.image || 'Disabled'}

Estimated cost: ${recommendation.estimated_cost_per_article.toFixed(4)}/article

) : ( // Questions view

Question {currentStep + 1} of {questions.length}

{currentQuestion.question}

handleAnswerChange(currentQuestion.id, value)} />
{currentStep > 0 && ( )}
)}
)} ); }; ``` --- ## 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:** ```json { "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:** ```json { "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:** ```json { "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:** ```php // 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 - [ ] **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) - [ ] **Integration** - [ ] Add button to settings page → "AI Model Configuration" card - [ ] Wire up `onApplyConfig` callback 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 1. **API Key Protection:** - Only server-side calls to OpenRouter (key never exposed to client) - REST endpoint requires `manage_options` capability 2. **Rate Limiting:** - Add WordPress nonce to prevent CSRF - Limit calls to 1 per minute per user 3. **Input Validation:** - Validate all user answers against whitelist - Reject invalid budget ranges ```php // 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) 1. **Refine system prompt** – Test with Claude to ensure it generates correct JSON 2. **Build backend endpoint** – Implement `/recommend-models` route with error handling 3. **Build React component** – Create modal UI with question flow 4. **Integration** – Wire up to settings page 5. **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