PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / trunk
AI Engine – The Chatbot, AI Framework & MCP for WordPress vtrunk
3.7.3 3.7.2 3.7.1 3.7.0 3.6.9 3.6.8 3.6.7 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 / labs / model-audit.php
ai-engine / labs Last commit date
mcp-core.php 2 weeks ago mcp-oauth.php 2 weeks ago mcp-rest.php 2 months ago mcp.conf 1 year ago mcp.php 3 weeks ago model-audit.php 3 days ago workspace-mock.html 1 month ago wpai-connectors.php 3 months ago wpai-gateway-availability.php 4 months ago wpai-gateway-directory.php 4 months ago wpai-gateway-image-model.php 4 months ago wpai-gateway-model.php 4 months ago wpai-gateway-providers.php 4 months ago wpai-gateway.php 4 months ago
model-audit.php
155 lines
1 <?php
2 /**
3 * Model coverage audit for the dynamic-model engines (Google, OpenRouter, …).
4 *
5 * Those engines build their model list at "Refresh Models" time from the provider's own
6 * API, then guess each model's nice name, features, tags and tools with a pile of
7 * regexes. When a provider ships a new naming shape those regexes quietly miss, and the
8 * model either vanishes from the dropdown or shows up without the capabilities it has.
9 * That is invisible until a user complains, so this audits it.
10 *
11 * Run it from the WordPress root:
12 * wp eval-file labs/model-audit.php # every dynamic engine
13 * wp eval-file labs/model-audit.php google # one engine type
14 *
15 * Findings are heuristics, not verdicts. Read them, then check the provider's docs
16 * before touching the regexes in classes/engines/<engine>.php.
17 */
18
19 global $mwai;
20 $core = $mwai->core;
21 $only = $args[0] ?? null;
22
23 // Engines whose models come from the provider API rather than constants/models.php.
24 $dynamic_types = [ 'google', 'openrouter' ];
25
26 // Engines where WE build the display name from the raw id. OpenRouter hands us a curated
27 // name ("Google: Gemini 3 Flash Preview") and we pass it straight through, so the name
28 // heuristics below would flag every one of its models forever.
29 $formats_own_names = [ 'google' ];
30
31 $issues_total = 0;
32
33 foreach ( (array) $core->get_option( 'ai_envs' ) as $env ) {
34 $type = $env['type'] ?? '';
35 if ( !in_array( $type, $dynamic_types, true ) ) {
36 continue;
37 }
38 if ( $only && $type !== $only ) {
39 continue;
40 }
41 if ( empty( $env['apikey'] ) ) {
42 echo "SKIP {$type} ({$env['name']}): no API key\n";
43 continue;
44 }
45
46 echo "\n=== {$type} / {$env['name']} ===\n";
47
48 try {
49 $engine = Meow_MWAI_Engines_Factory::get( $core, $env['id'] );
50 $models = $engine->retrieve_models();
51 }
52 catch ( Exception $e ) {
53 echo " ERROR: " . $e->getMessage() . "\n";
54 continue;
55 }
56
57 echo " models classified: " . count( $models ) . "\n";
58 $issues = [];
59
60 // 1. Duplicate display names. Google ships preview and GA variants of the same model
61 // and the name formatter strips the suffix from both, which makes the dropdown
62 // ambiguous. Anything still colliding here needs disambiguating.
63 $by_name = [];
64 foreach ( $models as $m ) {
65 $by_name[$m['name']][] = $m['model'];
66 }
67 foreach ( $by_name as $name => $ids ) {
68 if ( count( $ids ) > 1 ) {
69 $issues[] = "duplicate name \"{$name}\" for: " . implode( ', ', $ids );
70 }
71 }
72
73 // 2. Names that clearly fell through the formatter: still carrying a raw id, or a
74 // lowercased fragment that should have become a proper word. Only meaningful for the
75 // engines that format their own names.
76 if ( in_array( $type, $formats_own_names, true ) ) {
77 foreach ( $models as $m ) {
78 if ( $m['name'] === $m['model'] ) {
79 $issues[] = "unformatted name for {$m['model']} (name === id)";
80 }
81 else if ( preg_match( '/(preview|latest|exp|[0-9]{4}-[0-9]{2}|customtools|nano-banana)/i', $m['name'] )
82 && !preg_match( '/\(/', $m['name'] ) ) {
83 $issues[] = "raw id fragment left in name \"{$m['name']}\" ({$m['model']})";
84 }
85 }
86 }
87
88 // 3. Capability mismatches: the id advertises something the features do not.
89 // These are the exact shapes that were silently wrong before 2026-07-26. Each engine
90 // names its features in its own vocabulary (Google says 'image-generation', OpenRouter
91 // says 'text-to-image'), so any one of the listed synonyms satisfies the expectation.
92 $expectations = [
93 // id pattern => any one of these features is enough
94 '/-image(-preview)?$|nano-banana/' => [ 'image-generation', 'text-to-image' ],
95 '/embedding/' => [ 'embedding', 'embeddings' ],
96 '/(native-audio|-live-)/' => [ 'realtime' ],
97 '/(veo|video)/' => [ 'video-generation', 'text-to-video' ],
98 ];
99 foreach ( $models as $m ) {
100 $features = (array) ( $m['features'] ?? [] );
101 foreach ( $expectations as $pattern => $accepted ) {
102 if ( preg_match( $pattern, $m['model'] ) && !array_intersect( $accepted, $features ) ) {
103 $issues[] = "{$m['model']} looks like '{$accepted[0]}' but features are: "
104 . ( implode( ',', $features ) ?: '(none)' );
105 }
106 }
107 }
108
109 // 4. Models with no usable feature at all: they will render in the dropdown and then
110 // fail at query time.
111 foreach ( $models as $m ) {
112 if ( empty( $m['features'] ) ) {
113 $issues[] = "{$m['model']} has no features";
114 }
115 }
116
117 // 5. Anything the provider exposes that never made it into the list. Only the engine
118 // knows the raw payload, so this compares against the provider call where possible.
119 if ( $type === 'google' ) {
120 $res = wp_remote_get(
121 'https://generativelanguage.googleapis.com/v1beta/models?pageSize=200&key=' . $env['apikey'],
122 [ 'timeout' => 30 ]
123 );
124 if ( !is_wp_error( $res ) ) {
125 $body = json_decode( wp_remote_retrieve_body( $res ), true );
126 $classified = array_column( $models, 'model' );
127 foreach ( (array) ( $body['models'] ?? [] ) as $raw ) {
128 $id = str_replace( 'models/', '', $raw['name'] );
129 $methods = (array) ( $raw['supportedGenerationMethods'] ?? [] );
130 // Only care about ids we would plausibly want to offer.
131 $wanted = preg_match( '/^(gemini|nano-banana|imagen|veo)/', $id )
132 && ( in_array( 'generateContent', $methods, true ) || in_array( 'embedContent', $methods, true ) );
133 // Deliberate exclusions: dated snapshots, TTS, robotics.
134 $excluded = preg_match( '/-(tts|robotics)|(preview|exp)-\d{2}-\d{2,4}$|-\d{8}$/', $id );
135 if ( $wanted && !$excluded && !in_array( $id, $classified, true ) ) {
136 $issues[] = "provider exposes {$id} (" . implode( ',', $methods ) . ") but it was dropped";
137 }
138 }
139 }
140 }
141
142 if ( empty( $issues ) ) {
143 echo " no issues found\n";
144 }
145 else {
146 $issues = array_values( array_unique( $issues ) );
147 $issues_total += count( $issues );
148 foreach ( $issues as $i ) {
149 echo " ISSUE: {$i}\n";
150 }
151 }
152 }
153
154 echo "\nTOTAL ISSUES: {$issues_total}\n";
155