Backend improvements: - Add cache auto-clear on settings save (class-settings-v2.php) - Hooks updated_option action - Clears connection test transients when local backend settings change - Add reasoning_content streaming support (class-local-backend-provider.php) - Handles thinking models like Claude extended thinking - Captures chunk['choices'][0]['delta']['reasoning_content'] Documentation: - Add FRONTEND_AND_CHAT_FIX_SUMMARY.md with all fixes - Add FRONTEND-REFACTOR-PHASE2.md with modularization plan Note: Splitting effort deferred - will continue iterating on monolith Next: Fix session history not appearing bug
7.1 KiB
WP Agentic Writer – Fix Summary: Chat & Frontend Build
Date: 2026-06-16 (original) / 2026-06-17 (updates) Status: 🟢 RESOLVED
This document serves as a hands-off record of the debugging steps, root causes, and fixes applied to resolve the "empty chat response" issue and establish a proper frontend build pipeline.
Updates (2026-06-17)
Additional Backend Improvements
- Connection Test Caching - Already existed in
class-provider-manager.php(5-min TTL via transients) - Cache Auto-Clear on Settings Save - Added to
class-settings-v2.php- Hooks to
updated_optionaction - Clears connection test cache when local backend settings change
- Hooks to
- Reasoning Content Parsing - Added to
class-local-backend-provider.php- Captures
reasoning_contentfrom thinking models - Debug logging included
- Captures
Build Validation ✅
| Check | Result |
|---|---|
npm run build |
✅ Passes |
| PHP syntax (all files) | ✅ Passes |
| Build output | dist/sidebar.js (169 KB) |
Important: Two sidebar.js Files
| File | Loaded by PHP? |
|---|---|
assets/js/sidebar.js (438 KB) |
❌ Legacy, NOT loaded |
assets/js/dist/sidebar.js (169 KB) |
✅ Current, loaded |
Original Fixes (2026-06-16)
1. Frontend Build Pipeline Established
Problem:
The PHP plugin explicitly loads assets/js/dist/sidebar.js. However, ongoing refactoring work was occurring in assets/js/src/index.jsx. Because there was no active build process, changes made in index.jsx were not reflecting in the application.
Solution:
- Created
package.jsonwithesbuildto handle fast JSX compilation. - Added
scripts/build.jsconfigured specifically for WordPress Gutenberg (usingwp.element.createElementandwp.element.Fragment). - Critical Fix: Ensured the esbuild configuration wraps the output in an anonymous IIFE (
format: "iife") without defining aglobalName: "wp". A named global would have caused the compiled script to overwrite WordPress's nativewindow.wpobject withundefined, breaking the editor.
Usage:
- Run
npm run buildto compilesrc/index.jsx->dist/sidebar.js. - Run
npm run build:watchduring active development.
2. Backend Fix: PHP Fatal Error on Connection Test
Problem: When the local backend provider received an HTTP 4xx/5xx error (e.g., when trying to test a connection to a rate-limited or failing model), the entire REST request crashed, resulting in a 500 server error and breaking the chat flow entirely.
Root Cause:
In includes/class-local-backend-provider.php (line 882), a double-quoted string was used for a sprintf format:
__("API Error (HTTP %1$d): %2$s", "wp-agentic-writer")
Because it was double-quoted, PHP attempted to interpolate $d and $s as variables, resolving them to empty strings. The resulting string API Error (HTTP %1): %2 caused sprintf to throw a fatal ValueError: Unknown format specifier ")".
Fix:
Changed the double quotes to single quotes to prevent PHP variable interpolation, preserving the intended positional specifiers:
__('API Error (HTTP %1$d): %2$s', "wp-agentic-writer")
Scanned the rest of the codebase and confirmed this dangerous pattern does not exist anywhere else.
3. Backend Fix: Agentic Models Returning "Empty Responses"
Problem: The user experienced intermittent "empty chat response" errors even when the API connection was successful (HTTP 200).
Root Cause:
The configured models (e.g., dough/kr/claude-sonnet-4.5-thinking and ag/gemini-3-flash-agent) are agentic/tool-calling models. When fed a large WordPress system prompt asking for plain prose, these models burned all their tokens on internal "reasoning", attempted to emit a function/tool call, failed (finish_reason: "malformed_function_call"), and returned zero text content.
Because the streaming buffer received no content, it fell back to non-streaming, which also yielded zero content, triggering a generic "empty response" error.
Fix:
- Better Error Surfacing: Updated the streaming and fallback logic in
class-local-backend-provider.phpto actively capture thefinish_reasonpayload. - If the provider returns empty content but has a finish reason of
malformed_function_call,tool_calls, orfunction_call, the backend now intercepts this and throws a highly specific, actionable error:"The selected model [Model Name] returned no text (finish reason: tool/function call). This usually means an agentic/coding model is being used for prose. Choose a standard chat model (without an -agent or -agentic suffix) in Settings."
- Config Alignment: Used WP-CLI to update the WordPress options table, switching the local backend models from the agentic variants to the standard
gemini/gemini-3-flash-previewfor all prose tasks (chat,writing,planning, etc.).
Summary of Touched Files
package.json(New)scripts/build.js(New)assets/js/dist/sidebar.js(Rebuilt)includes/class-local-backend-provider.php(Fixed string interpolation, addedfinish_reasonedge-case handling, added error payload parsing).
The chat functionality is now robust, gracefully handles agentic model failures, and the React frontend can be reliably compiled from index.jsx.
4. Next Recommendations
With the frontend build pipeline established and the critical backend crashes resolved, the foundation is stable. Here are the recommended next steps:
-
Complete the React File Splitting (Refactor Phase 2) ⚠️ IN PROGRESS
- Now that
index.jsxcompiles successfully todist/sidebar.js, the massive 11,000+ lineindex.jsxshould be split into modular React components. - See
FRONTEND-REFACTOR-PHASE2.mdfor the detailed modularization plan. - The new
esbuildsetup natively supports resolving local imports, meaning you can safely extract components into asrc/components/directory and import them intoindex.jsxwithout changing the PHP backend. - Key clarification: The source is
src/index.jsx, which compiles todist/sidebar.js. We are NOT creating a newsidebar.jsxsource file - we are modularizing the existingindex.jsx.
- Now that
-
Optimize
test_connection()in the Provider Manager ✅ COMPLETED- Implemented connection test caching using WordPress transients with 5-minute TTL in
class-provider-manager.php. - Added automatic cache clearing in
class-settings-v2.phpwhen local backend settings are saved (URL, API key, model changes). - This eliminates redundant connection tests on every chat request, significantly reducing latency.
- Implemented connection test caching using WordPress transients with 5-minute TTL in
-
Handle Reasoning Tokens for Thinking Models ✅ COMPLETED
- Added
reasoning_contentparsing tochat_stream()method inclass-local-backend-provider.php. - The streaming parser now captures
chunk["choices"][0]["delta"]["reasoning_content"]from thinking models like Claude extended thinking. - Reasoning content is passed through the callback so the frontend can optionally display it in a collapsible section.
- Debug logging added to help identify when reasoning chunks are received.
- Added