PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 3.6.4
AI Engine – The Chatbot, AI Framework & MCP for WordPress v3.6.4
3.6.6 3.6.4 3.6.5 3.6.3 3.6.2 3.6.1 3.6.0 3.5.9 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.9 3.4.8 3.4.7 0.2.1 1.6.91 0.2.2 1.6.92 0.2.3 1.6.93 0.2.4 1.6.94 0.2.5 1.6.95 0.2.6 1.6.96 0.2.7 1.6.97 0.2.8 1.6.98 0.2.9 1.6.99 0.3.0 1.7.0 0.3.1 1.7.1 0.3.2 1.7.2 0.3.3 1.7.3 0.3.4 1.7.4 0.3.5 1.7.5 0.3.6 1.7.6 0.4.0 1.7.7 0.4.1 1.7.8 0.4.2 1.7.9 0.4.3 1.8.0 0.4.4 1.8.1 0.4.5 1.8.2 0.4.6 1.8.3 0.4.7 1.8.4 0.4.8 1.8.5 0.4.9 1.8.6 0.5.0 1.8.7 0.5.1 1.8.8 0.5.2 1.8.9 0.5.3 1.9.0 0.5.4 1.9.1 0.5.5 1.9.2 0.5.6 1.9.3 0.5.7 1.9.4 0.5.8 1.9.5 0.5.9 1.9.6 0.6.0 1.9.7 0.6.1 1.9.8 0.6.2 1.9.81 0.6.3 1.9.82 0.6.4 1.9.83 0.6.5 1.9.84 0.6.6 1.9.85 0.6.7 1.9.86 0.6.8 1.9.87 0.6.9 1.9.88 0.7.0 1.9.89 0.7.1 1.9.90 0.7.2 1.9.91 0.7.3 1.9.92 0.7.4 1.9.93 0.7.5 1.9.94 0.7.6 1.9.95 0.7.7 1.9.96 0.7.8 1.9.97 0.7.9 1.9.98 0.8.0 1.9.99 0.8.1 2.0.0 0.8.2 2.0.1 0.8.3 2.0.2 0.8.4 2.0.3 0.8.5 2.0.4 0.8.6 2.0.5 0.8.7 2.0.6 0.8.8 2.0.7 0.8.9 2.0.8 0.9.0 2.0.9 0.9.2 2.1.0 0.9.3 2.1.1 0.9.4 2.1.2 0.9.5 2.1.3 0.9.6 2.1.4 0.9.7 2.1.5 0.9.8 2.1.6 0.9.81 2.1.7 0.9.82 2.1.8 0.9.83 2.1.9 0.9.84 2.2.0 0.9.85 2.2.1 0.9.86 2.2.2 0.9.87 2.2.3 0.9.88 2.2.4 0.9.89 2.2.5 0.9.9 2.2.51 0.9.91 2.2.52 0.9.92 2.2.53 0.9.93 2.2.54 0.9.94 2.2.56 0.9.95 2.2.57 0.9.96 2.2.6 0.9.97 2.2.60 0.9.98 2.2.61 0.9.99 2.2.62 1.0.0 2.2.63 1.0.01 2.2.70 1.0.1 2.2.80 1.0.2 2.2.81 1.0.3 2.2.90 1.0.4 2.2.91 1.0.5 2.2.92 1.0.6 2.2.93 1.0.7 2.2.94 1.0.8 2.2.95 1.0.9 2.3.0 1.1.0 2.3.1 1.1.1 2.3.2 1.1.2 2.3.3 1.1.3 2.3.4 1.1.4 2.3.5 1.1.5 2.3.6 1.1.6 2.3.7 1.1.7 2.3.8 1.1.8 2.3.9 1.1.9 2.4.0 1.2.0 2.4.1 1.2.1 2.4.2 1.2.2 2.4.3 1.2.21 2.4.4 1.2.3 2.4.5 1.2.30 2.4.6 1.3.0 2.4.7 1.3.1 2.4.8 1.3.2 2.4.9 1.3.3 2.5.0 1.3.31 2.5.1 1.3.32 2.5.2 1.3.33 2.5.3 1.3.34 2.5.4 1.3.35 2.5.5 1.3.36 2.5.6 1.3.37 2.5.7 1.3.38 2.5.8 1.3.39 2.5.9 1.3.40 2.6.0 1.3.41 2.6.1 1.3.42 2.6.2 1.3.43 2.6.3 1.3.44 2.6.5 1.3.45 2.6.6 1.3.46 2.6.7 1.3.47 2.6.8 1.3.48 2.6.9 1.3.49 2.7.0 1.3.50 2.7.1 1.3.51 2.7.2 1.3.52 2.7.3 1.3.53 2.7.4 1.3.54 2.7.5 1.3.56 2.7.6 1.3.57 2.7.7 1.3.58 2.7.8 1.3.59 2.7.9 1.3.60 2.8.0 1.3.61 2.8.1 1.3.62 2.8.2 1.3.63 2.8.3 1.3.64 2.8.4 1.3.65 2.8.5 1.3.66 2.8.6 1.3.67 2.8.7 1.3.68 2.8.8 1.3.69 2.8.9 1.3.70 2.9.0 1.3.71 2.9.1 1.3.72 2.9.2 1.3.73 2.9.3 1.3.74 2.9.4 1.3.75 2.9.5 1.3.76 2.9.6 1.3.77 2.9.7 1.3.78 2.9.8 1.3.79 2.9.9 1.3.80 3.0.0 1.3.81 3.0.1 1.3.82 3.0.2 1.3.83 3.0.3 1.3.84 3.0.4 1.3.85 3.0.5 1.3.86 3.0.6 1.3.87 3.0.7 1.3.88 3.0.8 1.3.89 3.0.9 1.3.90 3.1.0 1.3.91 3.1.1 1.3.92 3.1.2 1.3.93 3.1.3 1.3.94 3.1.4 1.3.95 3.1.5 1.3.96 3.1.6 1.3.97 3.1.7 1.3.98 3.1.8 1.3.99 3.1.9 1.4.0 3.2.0 1.4.1 3.2.1 1.4.2 3.2.2 1.4.3 3.2.3 1.4.4 3.2.4 1.4.5 3.2.5 1.4.6 3.2.6 1.4.7 3.2.7 1.4.8 3.2.8 1.4.9 3.2.9 1.5.0 3.3.0 1.5.1 3.3.1 1.5.2 3.3.2 1.5.3 3.3.3 1.5.4 3.3.4 1.5.5 3.3.5 1.5.6 3.3.6 1.5.7 3.3.7 1.5.8 3.3.8 1.5.9 3.3.9 1.6.0 3.4.0 1.6.1 3.4.1 1.6.2 3.4.2 1.6.3 3.4.3 1.6.5 3.4.4 1.6.51 3.4.5 1.6.52 3.4.6 1.6.53 1.6.54 1.6.55 1.6.56 1.6.57 1.6.58 1.6.59 1.6.60 1.6.61 1.6.62 1.6.63 1.6.64 1.6.65 1.6.66 1.6.67 1.6.68 trunk 1.6.69 0.0.1 1.6.70 0.0.2 1.6.71 0.0.3 1.6.72 0.0.4 1.6.73 0.0.5 1.6.74 0.0.6 1.6.75 0.0.7 1.6.76 0.0.8 1.6.77 0.0.9 1.6.78 0.1.0 1.6.79 0.1.1 1.6.81 0.1.2 1.6.82 0.1.3 1.6.83 0.1.4 1.6.84 0.1.5 1.6.85 0.1.6 1.6.86 0.1.7 1.6.87 0.1.8 1.6.88 0.1.9 1.6.89 0.2.0 1.6.90
ai-engine / notes / STUDY-WPAI-LEARNINGS.md
ai-engine / notes Last commit date
CHANGELOG-PROPOSAL.txt 2 days ago QA-LOOP.md 2 days ago STUDY-CONSOLIDATION.md 2 days ago STUDY-MCP.md 2 days ago STUDY-WORKSPACE.md 2 days ago STUDY-WPAI-LEARNINGS.md 2 days ago
STUDY-WPAI-LEARNINGS.md
312 lines
1 # Open architecture study: lessons from WP AI Client
2
3 **Status:** thinking, not implementing.
4 **Owner:** Jordy.
5 **Started:** 2026-04-22.
6 **Trigger:** building the WP AI Client gateway in `/labs/` (commits
7 `4e5f9398`, `e14ee516`) exposed real shape mismatches between AI Engine's
8 internal data model and a modern, typed AI SDK.
9
10 This document is a working proposal, not a roadmap. It captures what we
11 learned from building the gateway, sketches a typed message/result layer
12 that AI Engine could grow into, and lists the smaller patterns worth
13 borrowing. Read before any large refactor of the query / reply layer.
14
15 ---
16
17 ## 1. What the gateway exposed
18
19 When mapping AiClient → AI Engine, three frictions kept showing up:
20
21 ### a. Lossy message shape
22
23 AI Engine messages are flat:
24
25 ```php
26 $query->messages = [
27 [ 'role' => 'user', 'content' => 'Hi.' ],
28 [ 'role' => 'assistant', 'content' => 'Hi back.' ],
29 ];
30 $query->add_file( DroppedFile::from_url( $url, 'analysis' ) );
31 ```
32
33 Files attach to the **whole query**, not to specific turns. This means a
34 multi-turn conversation that interleaves images ("here's image A, your
35 thoughts? — okay, here's image B, compare them") loses position. Engines
36 have to guess where the file belongs. The gateway worked around this
37 with a `[attachment]` placeholder string, which is the kind of hack you
38 write because the data model can't express the truth.
39
40 WP AI Client's `Message`/`MessagePart` solves this naturally:
41
42 ```php
43 new UserMessage([
44 new MessagePart( 'Compare these:' ),
45 new MessagePart( $imageA ),
46 new MessagePart( $imageB ),
47 ]);
48 ```
49
50 ### b. Reply shape is a property bag
51
52 `Meow_MWAI_Reply` exposes:
53
54 - `$reply->result` (string, but might be markdown image-tag soup for image queries)
55 - `$reply->results` (array — embeddings? URLs? text alternatives?)
56 - `$reply->usage` (associative array)
57 - `$reply->needFeedbacks`, `$reply->needClientActions` (queues)
58
59 It works, but every consumer reaches into different fields depending on
60 the query type. The gateway has to normalise this twice:
61
62 - text → take `$reply->result`, wrap in a `MessagePart` text
63 - image → take `$reply->results`, wrap each as a `MessagePart` file
64
65 A typed `Meow_MWAI_Data_Reply` with explicit `candidates`, `usage`, and
66 `finish_reason` would let consumers (gateway, REST endpoints, Insights)
67 read the same fields regardless of operation.
68
69 ### c. Capability is implicit
70
71 A model's tags array (`['core', 'chat', 'vision', 'functions', 'json']`)
72 declares broad capability, but engines do per-tag checks at dispatch
73 time. The gateway directory recently added the same tag-based
74 capability/option mapping the engines do — duplicated logic.
75
76 If the model object itself carried typed capability metadata, both the
77 engine and the gateway could read it from one place.
78
79 ---
80
81 ## 2. The typed data layer (the main proposal)
82
83 A new namespace under `/classes/data/`. Additive — nothing existing
84 breaks. Existing arrays continue to work; new code can opt in to the
85 typed surface.
86
87 ### 2.1 Message DTOs
88
89 ```php
90 // classes/data/message.php
91 class Meow_MWAI_Data_Message {
92 public string $role; // 'user' | 'assistant' | 'system'
93 public array $parts = []; // list<Meow_MWAI_Data_MessagePart>
94
95 public static function user( array $parts ): self { ... }
96 public static function assistant( array $parts ): self { ... }
97 public static function system( string $text ): self { ... }
98 public function add_part( Meow_MWAI_Data_MessagePart $p ): self { ... }
99 public function to_array(): array { /* legacy [role, content] form */ }
100 }
101
102 // classes/data/message-part.php
103 class Meow_MWAI_Data_MessagePart {
104 public string $type; // 'text' | 'file' | 'function_call' | 'function_result'
105 public ?string $text = null;
106 public ?Meow_MWAI_Query_DroppedFile $file = null;
107 public ?Meow_MWAI_Data_FunctionCall $function_call = null;
108 public ?Meow_MWAI_Data_FunctionResult $function_result = null;
109
110 public static function text( string $t ): self { ... }
111 public static function file( Meow_MWAI_Query_DroppedFile $f ): self { ... }
112 public static function function_call( Meow_MWAI_Data_FunctionCall $c ): self { ... }
113 public static function function_result( Meow_MWAI_Data_FunctionResult $r ): self { ... }
114 }
115 ```
116
117 `Meow_MWAI_Data_FunctionCall` and `Meow_MWAI_Data_FunctionResult` already
118 exist (`classes/data/function-call.php`, `classes/data/function-result.php`).
119 They slot in cleanly.
120
121 ### 2.2 Query side
122
123 `Meow_MWAI_Query_Text` gains an additive setter:
124
125 ```php
126 public function set_typed_messages( array $messages ): void {
127 // accepts list<Meow_MWAI_Data_Message>
128 // internally, also writes the legacy ->messages array via to_array() so
129 // every existing engine keeps working unchanged
130 $this->typed_messages = $messages;
131 $this->messages = array_map( fn($m) => $m->to_array(), $messages );
132 }
133 ```
134
135 Engines that want richer data (per-message file attachments, etc.) read
136 `$query->typed_messages` when present. Engines that don't read `$query->messages`
137 exactly as today.
138
139 ### 2.3 Reply side
140
141 ```php
142 // classes/data/reply-candidate.php
143 class Meow_MWAI_Data_ReplyCandidate {
144 public Meow_MWAI_Data_Message $message;
145 public string $finish_reason; // 'stop' | 'length' | 'content_filter' | 'tool_calls' | 'error'
146 }
147
148 // classes/data/token-usage.php
149 class Meow_MWAI_Data_TokenUsage {
150 public int $prompt_tokens;
151 public int $completion_tokens;
152 public int $total_tokens;
153 public ?int $thought_tokens; // for reasoning models
154 }
155 ```
156
157 `Meow_MWAI_Reply` gains optional typed accessors:
158
159 ```php
160 public function get_candidates(): array { /* list<ReplyCandidate> */ }
161 public function get_token_usage(): Meow_MWAI_Data_TokenUsage { ... }
162 public function get_finish_reason(): string { ... }
163 ```
164
165 These derive from existing fields when set the old way. Engines can
166 populate them directly when constructing a fresh Reply.
167
168 ### 2.4 What this buys us
169
170 - **Gateway thinness.** `wpai-gateway-model.php::extract_parts` and
171 `build_result` collapse to one-liners.
172 - **Vision/audio/document support** without per-engine guesswork.
173 - **Function calling** flows through typed parts instead of side-channel
174 arrays (`needFeedbacks`).
175 - **Streaming events** can carry `MessagePart` deltas instead of raw
176 strings. Less ambiguity about what part of the response just changed.
177 - **Insights** can record per-modality stats cleanly.
178
179 ### 2.5 Migration strategy
180
181 This is the important part. AI Engine has thousands of installs.
182 Breaking the API is not on the table.
183
184 1. **Phase A — parallel layer.** Add the DTOs. No engine reads them yet.
185 Add accessors on Reply that derive from existing fields. Tests + docs.
186 2. **Phase B — gateway adopts.** Refactor `wpai-gateway-model.php` to
187 build typed messages and read typed candidates. This is a low-risk
188 pilot since the gateway is in `/labs/` and opt-in.
189 3. **Phase C — one engine adopts.** Pick the smallest engine
190 (`anthropic.php` is well-bounded) and have it consume typed messages
191 when present. Keep the legacy code path. Compare behavior.
192 4. **Phase D — REST endpoints adopt.** New endpoints (or new params on
193 existing ones) accept typed payloads. Old payloads keep working.
194 5. **Phase E — engines opt in one by one** as time allows. Years, not
195 weeks. The legacy path stays compatible forever.
196
197 No deprecations until phase E completes everywhere.
198
199 ---
200
201 ## 3. Other patterns worth borrowing (smaller)
202
203 These are independent and can be picked up à la carte.
204
205 ### a. Capability matching before dispatch
206
207 WP AI Client builds `ModelRequirements::fromPromptData(...)` and matches
208 it against `ModelMetadata::getSupportedCapabilities()` *before* sending
209 anything. We do this implicitly via tag checks scattered across engines.
210
211 A central `Meow_MWAI_Services_CapabilityMatcher` could:
212
213 - Take a query.
214 - Find what features it needs (vision input, JSON output, function tools).
215 - Cross-check against the resolved model's tag set.
216 - Throw a clean "model X doesn't support Y" exception up front.
217
218 Saves users from cryptic provider errors.
219
220 ### b. Fluent prompt builder (additive)
221
222 A new `Meow_MWAI_Prompt::create('My text')->with_system('...')->with_max_tokens(500)->run()`
223 surface, layered on top of `simpleTextQuery`. Old API stays. New API
224 reads better.
225
226 ### c. Enums for finish reasons, modalities, options
227
228 Replace string literals like `'stop'` / `'length'` / `'tool_calls'` with
229 class constants. Helps autocomplete in IDEs and prevents typos. Low
230 priority.
231
232 ### d. Provider availability separated from configuration
233
234 WP AI Client's `ProviderAvailabilityInterface::isConfigured()` is a
235 runtime check, not a static one. AI Engine could expose
236 `Meow_MWAI_Services_EnvAvailability::is_ready( $env_id )` that checks
237 key + reachability, useful for fallback chains.
238
239 ---
240
241 ## 4. Things NOT to copy
242
243 These are areas where AI Engine is already ahead — don't regress.
244
245 - **Streaming.** WP AI Client has no streaming hook. AI Engine's
246 `$core->run_query( $q, $cb )` is years more mature.
247 - **Provider plugin per provider.** WP requires a separate plugin per
248 provider. AI Engine bundles them. Splitting would create more support
249 burden, not less.
250 - **Single key per provider.** WP allows one config per provider; AI
251 Engine has multi-environment, which real teams need (staging vs prod,
252 client A vs client B).
253 - **Tool calling via separate side channel.** WP AI Client treats tools
254 as a special option. AI Engine's `mwai_functions_list` /
255 `mwai_ai_feedback` filter pair is more WordPress-native and lets
256 plugins extend without subclassing.
257 - **No Insights, no rate limiting, no MCP.** These are AI Engine's
258 reason to exist. Don't dilute.
259
260 ---
261
262 ## 5. Reading list before any work starts
263
264 Before opening this back up, re-read:
265
266 - `/labs/wpai-gateway-model.php` — see `extract_parts()` for the kind of
267 conversion the typed layer would eliminate.
268 - `/labs/wpai-gateway-directory.php``make_metadata()` mirrors what
269 WP AI Client expects.
270 - `/Users/meow/sites/seven/app/public/wp-includes/php-ai-client/src/Messages/DTO/Message.php`
271 — reference shape for message DTOs.
272 - `/Users/meow/sites/seven/app/public/wp-includes/php-ai-client/src/Results/DTO/GenerativeAiResult.php`
273 — reference shape for typed results.
274 - `/Users/meow/sites/seven/app/public/wp-content/plugins/ai-provider-for-anthropic/src/Models/AnthropicTextGenerationModel.php`
275 — full reference of how a typed model is built end-to-end.
276 - AI Engine: `classes/data/function-call.php` and
277 `classes/data/function-result.php` — existing examples of the DTO
278 pattern AI Engine already started.
279
280 ---
281
282 ## 6. Open questions
283
284 - Does `Meow_MWAI_Data_Message::to_array()` cleanly degrade when a turn
285 has only a file part? The gateway uses an `[attachment]` placeholder
286 today; the typed layer should be honest about what's there.
287 - How do we signal *which* candidate a tool call came from when an engine
288 returns multiple candidates with different tool calls? (Anthropic does;
289 Google often does for parallel calls.)
290 - Do we want to expose typed messages in the public PHP API
291 (`Meow_MWAI_API`) immediately, or keep it internal first?
292 - Is `finish_reason: 'tool_calls'` enough, or do we want richer state
293 for partially-completed tool loops?
294
295 These don't block the proposal; they're worth thinking about before
296 phase D.
297
298 ---
299
300 ## 7. When to revisit
301
302 Look at this doc when:
303
304 - Touching `classes/query/text.php`, `classes/reply.php`, or any engine.
305 - Adding a new modality (vision, audio, video, document) at the API layer.
306 - Building a new gateway/integration similar to WP AI Client.
307 - Designing a new query type.
308 - Considering a major refactor of the function-calling pipeline.
309
310 Otherwise, leave it alone. The current data model works for the cases it
311 already serves.
312