Skip to content

Add AI alt text generation using WordPress AI connector - #1742

Open
SteveJonesDev wants to merge 10 commits into
developfrom
claude/ai-alt-accessibility-checker-6p67bm
Open

Add AI alt text generation using WordPress AI connector#1742
SteveJonesDev wants to merge 10 commits into
developfrom
claude/ai-alt-accessibility-checker-6p67bm

Conversation

@SteveJonesDev

@SteveJonesDev SteveJonesDev commented Jun 10, 2026

Copy link
Copy Markdown
Member

Integrates AI-powered alt text suggestions into the issue modal for
image alt text violations (img_alt_missing, img_alt_empty, img_alt_invalid).

When a supported AI service is configured — either the WordPress AI Client
(WP 7.0+ wp_ai_client_prompt) or the AI Services plugin — a collapsible
"Generate AI Alt Text" panel appears inside the issue modal. Clicking
"Generate Suggestions" sends the image to the connected AI service and
returns three distinct alt text options, each focused on a different visual
element. Users can review character counts and explanations, then apply a
suggestion directly to the media attachment via the WP REST API. On apply,
a rescan is queued so the issue status updates automatically.

New files:

  • includes/classes/AI/AltTextGenerator.php — core AI integration class
  • src/issueModal/components/AltTextPanel.js — React suggestion UI

Modified:

  • includes/classes/class-rest-api.php — POST /accessibility-checker/v1/generate-alt-text endpoint
  • admin/class-enqueue-admin.php — exposes aiAltAvailable flag to JS
  • src/issueModal/components/IssueDetailsModal.js — mounts AltTextPanel
  • src/issueModal/sass/issue-modal.scss — styles for suggestion cards and panel

Summary by CodeRabbit

  • New Features
    • AI-powered alt-text generation: fetch multiple sanitized suggestions with explanations and focus hints for images with missing/invalid alt text.
    • In-app Alt Text panel to generate, regenerate, review, and apply suggestions; shows character counts, “over 125” warnings, per-suggestion apply states, and non-dismissible success notices.
    • UI availability check and server-side generation; applying a suggestion updates the media item, refreshes editor image blocks when present, and triggers a rescan.

claude added 2 commits June 10, 2026 21:57
Integrates AI-powered alt text suggestions into the issue modal for
image alt text violations (img_alt_missing, img_alt_empty, img_alt_invalid).

When a supported AI service is configured — either the WordPress AI Client
(WP 7.0+ wp_ai_client_prompt) or the AI Services plugin — a collapsible
"Generate AI Alt Text" panel appears inside the issue modal. Clicking
"Generate Suggestions" sends the image to the connected AI service and
returns three distinct alt text options, each focused on a different visual
element. Users can review character counts and explanations, then apply a
suggestion directly to the media attachment via the WP REST API. On apply,
a rescan is queued so the issue status updates automatically.

New files:
- includes/classes/AI/AltTextGenerator.php — core AI integration class
- src/issueModal/components/AltTextPanel.js — React suggestion UI

Modified:
- includes/classes/class-rest-api.php — POST /accessibility-checker/v1/generate-alt-text endpoint
- admin/class-enqueue-admin.php — exposes aiAltAvailable flag to JS
- src/issueModal/components/IssueDetailsModal.js — mounts AltTextPanel
- src/issueModal/sass/issue-modal.scss — styles for suggestion cards and panel
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds AI-generated alt-text: a backend AltTextGenerator (dual-provider), a POST REST endpoint, a frontend AltTextPanel with apply flows and styles, modal integration, and a localized aiAltAvailable flag for conditional UI display.

Changes

AI-Powered Alt-Text Generation

Layer / File(s) Summary
AI Alt-Text Generator Backend
includes/classes/AI/AltTextGenerator.php
AltTextGenerator class detects available AI providers (WordPress AI Client or AI Services), validates attachment IDs and image URLs, clamps suggestion counts (1–5), builds structured prompts, routes requests to provider-specific handlers, normalizes provider output, and parses JSON responses into sanitized suggestion objects with alt, focus, and explanation fields.
REST API Endpoint Registration and Handler
includes/classes/class-rest-api.php
New POST /accessibility-checker/v1/generate-alt-text endpoint validates attachment_id (required, positive integer) and num_suggestions (optional, 1–5, default 3), checks edit_post permission, calls AltTextGenerator::generate(), and returns 200 with suggestions or 400 with error message and code.
AltTextPanel React Component and Styling
src/issueModal/components/AltTextPanel.js, src/issueModal/sass/issue-modal.scss
New AltTextPanel component extracts attachment ID from issue markup, manages suggestion generation and apply state, calls REST endpoint to fetch suggestions, renders SuggestionCard sub-components showing suggested alt text with character count warnings (>125), optional focus/explanation, and apply buttons with loading/applied states; includes error/success notices and .edac-ai-alt-panel styling.
Modal Integration
src/issueModal/components/IssueDetailsModal.js
IssueDetailsModal imports AltTextPanel, introduces isAltPanelOpen state, and conditionally renders the panel in the modal's actions area.
Frontend Availability Localization
admin/class-enqueue-admin.php
Sidebar script's localized edac_sidebar_app data now includes aiAltAvailable boolean from AltTextGenerator::is_available(), enabling the frontend to conditionally display alt-text UI only when AI services are present.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Modal as IssueDetailsModal
  participant Panel as AltTextPanel
  participant REST as REST API
  participant Generator as AltTextGenerator
  participant MediaAPI as WP Media API
  User->>Modal: open issue modal
  Modal->>Panel: render AltTextPanel
  Panel->>Panel: extract attachment ID from markup
  User->>Panel: click Generate Suggestions
  Panel->>REST: POST /accessibility-checker/v1/generate-alt-text
  REST->>Generator: generate(attachment_id, num_suggestions)
  Generator->>Generator: validate, build prompt, call AI provider
  Generator->>REST: return suggestions
  REST->>Panel: 200 {suggestions: [...]}
  Panel->>Panel: display suggestion cards with alt text
  User->>Panel: click Apply on a suggestion
  Panel->>MediaAPI: POST /wp/v2/media/:id {alt: suggestion}
  MediaAPI->>Panel: 200 success
  Panel->>Modal: setPendingRescan(true)
  User->>Modal: close modal
  Modal->>Modal: re-scan issue to update status
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

codex

Suggested reviewers

  • pattonwebz

Poem

🐰 I nibble code beneath the moon,

I whisper alt for every tune,
Small words to name each captured view,
Suggestions sprout where pixels grew,
Accessibility hops anew.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately and concisely summarizes the primary feature—adding AI-powered alt text generation integrated with WordPress AI connector.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/ai-alt-accessibility-checker-6p67bm

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an AI-powered alt text generation feature for images, integrating with either the WordPress AI connector or the AI Services plugin. It adds a new REST API endpoint, a backend AltTextGenerator class, and a frontend AltTextPanel component within the issue details modal to allow users to generate and apply suggestions. The review feedback highlights two critical areas where defensive checks should be added to handle potential WP_Error returns from wp_ai_client_prompt() and $service->get_model() to prevent runtime errors.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +177 to +182
$model = $service->get_model(
[
'feature' => 'accessibility-checker-alt-text',
'capabilities' => [ 'MULTIMODAL_INPUT', 'TEXT_GENERATION' ],
]
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The $service->get_model() call can return a WP_Error if no matching model is found or if capabilities are not met. Calling $model->generate_text() on a WP_Error object will result in a fatal error (or a TypeError caught by the Throwable block). We should defensively check if $model is a WP_Error before proceeding.

			$model = $service->get_model(
				[
					'feature'      => 'accessibility-checker-alt-text',
					'capabilities' => [ 'MULTIMODAL_INPUT', 'TEXT_GENERATION' ],
				]
			);

			if ( is_wp_error( $model ) ) {
				return $model;
			}

Comment on lines +112 to +114
$builder = wp_ai_client_prompt( 'accessibility-checker-alt-text' );

if ( method_exists( $builder, 'with_image' ) ) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If wp_ai_client_prompt() returns a WP_Error (e.g., due to configuration issues), calling method_exists or $builder->send() will trigger a PHP error/exception. It is safer and more idiomatic to perform a defensive check using is_wp_error() immediately after retrieving the builder.

			$builder = wp_ai_client_prompt( 'accessibility-checker-alt-text' );

			if ( is_wp_error( $builder ) ) {
				return $builder;
			}

			if ( method_exists( $builder, 'with_image' ) ) {

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 91aa999624

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +170 to +173
await apiFetch( {
path: `/wp/v2/media/${ attachmentId }`,
method: 'POST',
data: { alt_text: altText },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the scanned markup when applying alt text

When the issue comes from saved post content such as a Gutenberg or classic image whose HTML already contains alt="" (or a bad alt), this only writes the suggestion to the media attachment record. The scanner checks the rendered DOM attribute in the post content, so the subsequent rescan will still see the same empty/invalid alt in the saved markup and the UI reports success even though the issue is not fixed. The apply path needs to update the block/post markup for the affected image, or only offer this action for images rendered dynamically from attachment metadata.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
includes/classes/class-rest-api.php (1)

379-386: ⚡ Quick win

The minimum and maximum properties are not enforced by WordPress REST API validation.

Lines 383-384 define minimum and maximum for num_suggestions, but WordPress REST API does not automatically enforce these constraints. They serve only as schema documentation. The backend AltTextGenerator::generate() already clamps the value (line 63 in AltTextGenerator.php), but adding a validation callback here would provide earlier feedback to API consumers.

✅ Proposed fix to enforce range validation
 							'num_suggestions' => [
 								'required'          => false,
 								'type'              => 'integer',
 								'default'           => 3,
 								'minimum'           => 1,
 								'maximum'           => 5,
+								'validate_callback' => function ( $param ) {
+									return is_numeric( $param ) && $param >= 1 && $param <= 5;
+								},
 								'sanitize_callback' => 'absint',
 							],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@includes/classes/class-rest-api.php` around lines 379 - 386, The schema for
'num_suggestions' in class-rest-api.php documents 'minimum' and 'maximum' but
doesn't enforce them; add a validate_callback for the 'num_suggestions' argument
(the same array where 'type' => 'integer' and 'sanitize_callback' => 'absint' is
defined) that checks the sanitized int is between 1 and 5 and returns true or a
WP_Error with a clear message; keep AltTextGenerator::generate() clamping as a
fallback but perform this early validation to give API consumers immediate
feedback.
includes/classes/AI/AltTextGenerator.php (1)

54-56: ⚡ Quick win

Verify the attachment is an image, not just any attachment type.

The validation checks get_post_type( $attachment_id ) === 'attachment' but doesn't verify the attachment is actually an image. Non-image attachments (PDFs, videos, audio files) would pass this check but fail when wp_get_attachment_image_url() returns false at line 58. Consider adding an explicit mime type check for clarity and better error messages.

📸 Proposed fix to validate image mime type
+		if ( ! wp_attachment_is_image( $attachment_id ) ) {
+			return new \WP_Error( 'not_an_image', __( 'The provided attachment is not an image.', 'accessibility-checker' ) );
+		}
+
 		if ( 'attachment' !== get_post_type( $attachment_id ) ) {
 			return new \WP_Error( 'invalid_attachment', __( 'The provided ID is not a valid media attachment.', 'accessibility-checker' ) );
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@includes/classes/AI/AltTextGenerator.php` around lines 54 - 56, The current
validation in AltTextGenerator (the block checking get_post_type($attachment_id)
=== 'attachment') only ensures it's an attachment but not that it's an image;
update the validation to explicitly confirm the attachment is an image (use
wp_attachment_is_image($attachment_id) or check
get_post_mime_type($attachment_id) starts with 'image/') and return a distinct
WP_Error (e.g., 'invalid_image_attachment') with a clear message when it is not
an image before calling wp_get_attachment_image_url(); keep the original
post-type check but add this image-type guard in the same method to prevent
wp_get_attachment_image_url() from receiving non-image IDs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@includes/classes/AI/AltTextGenerator.php`:
- Line 233: The json_decode call that assigns $data = json_decode( $text, true
); should include an explicit depth limit to avoid DoS from deeply nested JSON;
update the call in AltTextGenerator (the json_decode usage that produces $data)
to pass a depth of 3 (e.g., json_decode(..., true, 3)) so the decoded response
is restricted to the expected shallow structure while preserving associative
arrays.

In `@src/issueModal/components/AltTextPanel.js`:
- Around line 106-117: Reformat the JSDoc block for the AltTextPanel component
so it matches the project's ESLint JSDoc alignment rules: ensure the comment
starts with /** on its own line, every subsequent line begins with " * " (single
space after the asterisk), align the `@param` tags vertically and use a single
space between the type, param name and the hyphen/description, and close the
block with " */" on its own line; apply this to the block documenting
AltTextPanel and its params (props.rule, props.issue, props.isOpen,
props.onToggle).
- Around line 24-104: The SuggestionCard component has ESLint failures: fix the
JSDoc alignment for the SuggestionCard comment block so each tag line lines up;
add missing trailing commas in the multiline JSX expressions (the aria-label
sprintf calls and the className/props lists around the character count and focus
spans — locate the sprintf/aria-label blocks in SuggestionCard to add trailing
commas); and remove the nested ternary used to choose the Button text (inside
Button children around isApplied/isApplying) by computing the label beforehand
(e.g., create a local getButtonLabel or label const above the return and use a
simple conditional or if/else) so the JSX only renders a single variable and the
Spinner remains conditional. Ensure all changes reference the SuggestionCard
function and its Button/aria-label sprintf usages.

---

Nitpick comments:
In `@includes/classes/AI/AltTextGenerator.php`:
- Around line 54-56: The current validation in AltTextGenerator (the block
checking get_post_type($attachment_id) === 'attachment') only ensures it's an
attachment but not that it's an image; update the validation to explicitly
confirm the attachment is an image (use wp_attachment_is_image($attachment_id)
or check get_post_mime_type($attachment_id) starts with 'image/') and return a
distinct WP_Error (e.g., 'invalid_image_attachment') with a clear message when
it is not an image before calling wp_get_attachment_image_url(); keep the
original post-type check but add this image-type guard in the same method to
prevent wp_get_attachment_image_url() from receiving non-image IDs.

In `@includes/classes/class-rest-api.php`:
- Around line 379-386: The schema for 'num_suggestions' in class-rest-api.php
documents 'minimum' and 'maximum' but doesn't enforce them; add a
validate_callback for the 'num_suggestions' argument (the same array where
'type' => 'integer' and 'sanitize_callback' => 'absint' is defined) that checks
the sanitized int is between 1 and 5 and returns true or a WP_Error with a clear
message; keep AltTextGenerator::generate() clamping as a fallback but perform
this early validation to give API consumers immediate feedback.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9f126e93-32d7-45a3-b5f9-bb69c48a19c7

📥 Commits

Reviewing files that changed from the base of the PR and between cb1bb42 and 91aa999.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (6)
  • admin/class-enqueue-admin.php
  • includes/classes/AI/AltTextGenerator.php
  • includes/classes/class-rest-api.php
  • src/issueModal/components/AltTextPanel.js
  • src/issueModal/components/IssueDetailsModal.js
  • src/issueModal/sass/issue-modal.scss

Comment thread includes/classes/AI/AltTextGenerator.php Outdated
Comment thread src/issueModal/components/AltTextPanel.js
Comment thread src/issueModal/components/AltTextPanel.js
claude added 2 commits June 10, 2026 22:37
PHP (AltTextGenerator):
- Add is_wp_error() guard after wp_ai_client_prompt() call before chaining
  methods — previously a WP_Error return would cause a fatal TypeError
- Add is_wp_error() guard after get_model() call before calling generate_text()
  on the result for the same reason
- Pass depth limit of 3 to json_decode() to cap potential DoS from deeply
  nested AI responses (expected structure is a flat array of simple objects)

JS (AltTextPanel):
- After saving alt text to the media attachment record, also update any
  matching core/image Gutenberg block attribute via the block-editor data
  store — without this the saved post markup still carried the old (empty/
  invalid) alt so a rescan would continue to flag the issue despite the
  apparent success message

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/issueModal/components/AltTextPanel.js`:
- Around line 189-207: The current findImageBlock function returns only the
first matching core/image block so only one block gets updated; change the logic
to collect all matching blocks (e.g., rename to findImageBlocks or add a
collector) by recursively traversing blockEditorSelect.getBlocks() and gathering
every block where block.name === 'core/image' && block.attributes?.id ===
attachmentId (including matches inside innerBlocks), then iterate over the
collected blocks and call blockEditorDispatch.updateBlockAttributes for each
block.clientId with { alt: altText } so every instance of the attachment is
updated.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1b233891-8795-4cf4-bc51-a9ed9976474e

📥 Commits

Reviewing files that changed from the base of the PR and between 7dd2111 and e481c8c.

📒 Files selected for processing (2)
  • includes/classes/AI/AltTextGenerator.php
  • src/issueModal/components/AltTextPanel.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • includes/classes/AI/AltTextGenerator.php

Comment on lines +189 to +207
const findImageBlock = ( blocks ) => {
for ( const block of blocks ) {
if ( block.name === 'core/image' && block.attributes?.id === attachmentId ) {
return block;
}
if ( block.innerBlocks?.length ) {
const found = findImageBlock( block.innerBlocks );
if ( found ) {
return found;
}
}
}
return null;
};

const imageBlock = findImageBlock( blockEditorSelect.getBlocks() );
if ( imageBlock ) {
blockEditorDispatch.updateBlockAttributes( imageBlock.clientId, { alt: altText } );
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Update all matching core/image blocks, not just the first one.

At Line 189, findImageBlock short-circuits on the first match, so only one block gets updated at Line 206. If the same attachment appears multiple times in content, other blocks keep stale alt, and rescans may still fail.

Suggested fix
-					const findImageBlock = ( blocks ) => {
+					const findImageBlocks = ( blocks, matches = [] ) => {
 						for ( const block of blocks ) {
 							if ( block.name === 'core/image' && block.attributes?.id === attachmentId ) {
-								return block;
+								matches.push( block );
 							}
 							if ( block.innerBlocks?.length ) {
-								const found = findImageBlock( block.innerBlocks );
-								if ( found ) {
-									return found;
-								}
+								findImageBlocks( block.innerBlocks, matches );
 							}
 						}
-						return null;
+						return matches;
 					};
 
-					const imageBlock = findImageBlock( blockEditorSelect.getBlocks() );
-					if ( imageBlock ) {
-						blockEditorDispatch.updateBlockAttributes( imageBlock.clientId, { alt: altText } );
+					const imageBlocks = findImageBlocks( blockEditorSelect.getBlocks() );
+					for ( const imageBlock of imageBlocks ) {
+						blockEditorDispatch.updateBlockAttributes( imageBlock.clientId, { alt: altText } );
 					}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/issueModal/components/AltTextPanel.js` around lines 189 - 207, The
current findImageBlock function returns only the first matching core/image block
so only one block gets updated; change the logic to collect all matching blocks
(e.g., rename to findImageBlocks or add a collector) by recursively traversing
blockEditorSelect.getBlocks() and gathering every block where block.name ===
'core/image' && block.attributes?.id === attachmentId (including matches inside
innerBlocks), then iterate over the collected blocks and call
blockEditorDispatch.updateBlockAttributes for each block.clientId with { alt:
altText } so every instance of the attachment is updated.

claude added 2 commits June 11, 2026 18:06
Replace the single get_text() check with extract_text_from_response()
which tries all known method names across SDK versions in order:
get_text, get_first_candidate_text, get_content, camelCase variants,
and a get_candidates() traversal. Both the WP AI Client and ai-services
paths now share this fallback chain.

On failure the error message now includes the actual PHP class name of
the unexpected response object to help diagnose mismatched API versions.
The previous implementation used incorrect method names based on assumed
API shape. After inspecting the actual Prompt_Builder source:

wp_ai_client_prompt():
- First argument is the prompt text (not a feature slug)
- Image files go through ->with_file($url, $mimeType), not ->with_image()
- Terminate method is ->generate_text() which returns string|WP_Error
  directly — there is no ->with_message() or ->send()

Add get_mime_type() helper that derives MIME type from the image URL's
file extension so with_file() gets the correct type hint.

ai-services Candidates traversal:
- Replace the flat extract_text_from_response() with extract_text_from_candidates()
  that follows the documented chain:
  ->get(0)->get_content()->get_parts()->get(0)->get_text()
- Keep get_first_candidate_text() and direct string methods as fallbacks
  for older plugin versions

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
includes/classes/AI/AltTextGenerator.php (2)

35-36: ⚠️ Potential issue | 🟠 Major

Fix AltTextGenerator::is_available() so the UI flag doesn’t mean “configured/ready to generate”.

includes/classes/AI/AltTextGenerator.php:35-36 only checks function_exists() (integration present). admin/class-enqueue-admin.php:215 forwards this as aiAltAvailable, and src/issueModal/components/AltTextPanel.js:134 uses it to render the panel—even when no usable service is configured. In that case, generate() can still fail (e.g., generate_with_ai_services() returns WP_Error code no_configured_service).
Make is_available() (or a new method) validate an actually usable/configured connector/service before returning true, or rename the frontend flag to match what the check really guarantees.


217-237: ⚠️ Potential issue | 🟠 Major

Fix AI Services fallback capability mismatch in AltTextGenerator::generate_with_ai_services()

When the multimodal-capable service lookup fails, the fallback selects a TEXT_GENERATION-only service, but the code still requests a model with MULTIMODAL_INPUT and sends an image_url part. On text-only setups this defers failure to $service->get_model() and returns a lower-level error instead of the intended no_configured_service response—either keep MULTIMODAL_INPUT requirements throughout or implement a real text-only fallback that omits image_url/multimodal model requirements.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@includes/classes/AI/AltTextGenerator.php` around lines 217 - 237, The
fallback in AltTextGenerator::generate_with_ai_services() currently chooses a
TEXT_GENERATION-only service but still later calls $service->get_model()
requesting MULTIMODAL_INPUT and sends an image_url, causing misleading
lower-level errors; update the logic so that after calling
$ai->get_available_service(...) you detect the picked service's capabilities: if
it only supports TEXT_GENERATION, call $service->get_model() with capabilities [
'TEXT_GENERATION' ] and send a text-only prompt (omit image_url and any
multimodal parts); otherwise keep the MULTIMODAL_INPUT requirement; also ensure
the original no_configured_service WP_Error is still returned when no suitable
service exists.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@includes/classes/AI/AltTextGenerator.php`:
- Around line 166-185: The code in
AltTextGenerator::extract_text_from_response() calls method_exists() on
$response and on $first without ensuring they are objects, which can cause
warnings or exceptions for null/array/scalar SDK responses; update the method to
first verify is_object($response) before looping over $methods and before
calling method_exists(), and when handling candidates ensure $candidates is an
array and that $first is an object (use is_object($first)) before calling
method_exists($first, ...); if those checks fail, return the existing
WP_Error('unexpected_response') path so non-object responses are handled safely.

---

Outside diff comments:
In `@includes/classes/AI/AltTextGenerator.php`:
- Around line 217-237: The fallback in
AltTextGenerator::generate_with_ai_services() currently chooses a
TEXT_GENERATION-only service but still later calls $service->get_model()
requesting MULTIMODAL_INPUT and sends an image_url, causing misleading
lower-level errors; update the logic so that after calling
$ai->get_available_service(...) you detect the picked service's capabilities: if
it only supports TEXT_GENERATION, call $service->get_model() with capabilities [
'TEXT_GENERATION' ] and send a text-only prompt (omit image_url and any
multimodal parts); otherwise keep the MULTIMODAL_INPUT requirement; also ensure
the original no_configured_service WP_Error is still returned when no suitable
service exists.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e95abe31-21a1-4962-81de-e74d43fc98e5

📥 Commits

Reviewing files that changed from the base of the PR and between e481c8c and dc438f0.

📒 Files selected for processing (1)
  • includes/classes/AI/AltTextGenerator.php

Comment thread includes/classes/AI/AltTextGenerator.php Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
includes/classes/AI/AltTextGenerator.php (1)

35-36: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make is_available() reflect usable provider state, not just installed functions.

admin/class-enqueue-admin.php uses this method to set aiAltAvailable, so a bare function_exists() check makes the panel render even when no AI service/model is actually configured. In that state the first POST just comes back with no_configured_service, which is a user-visible broken flow. Reuse the same provider/configuration checks here that generation depends on so the UI only advertises the feature when it can succeed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@includes/classes/AI/AltTextGenerator.php` around lines 35 - 36,
is_available() currently only checks for installed helper functions and so
advertises the Alt Text UI even when no AI provider/model is configured; change
it to return true only when the same provider/configuration checks used by the
generation path indicate a usable service. Concretely, update
AI/AltTextGenerator::is_available() to call the same configuration/provider
helper(s) used by the POST-generation flow (the code path that currently returns
"no_configured_service") — e.g. check the ai_services() result for a
configured/active provider or call the existing helper that verifies an AI
service is configured — and only return true when those checks pass (in addition
to any required function_exists checks) so admin/class-enqueue-admin.php's
aiAltAvailable reflects an actually usable service.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@includes/classes/AI/AltTextGenerator.php`:
- Around line 126-131: The docblock long description for wp_ai_client_prompt()
in AltTextGenerator.php starts with a lowercase word causing PHPCS failure;
update the long description so its first character is capitalized (e.g., change
"wp_ai_client_prompt()" to "Wp_ai_client_prompt()" or otherwise ensure the first
letter of the long description is uppercase) while leaving the rest of the
signature and builder method lines (->with_file, ->generate_text) unchanged.
- Around line 145-157: The AltTextGenerator currently proceeds to call
generate_text() and parse_suggestions() even when the builder lacks multimodal
support; update the control flow in AltTextGenerator (around the with_file /
generate_text block) to fail fast or explicitly switch to a true text-only mode
when method_exists($builder, 'with_file') is false—return a WP_Error (or a clear
text-only flag) instead of producing image-grounded suggestions; likewise, in
the AI Services selection path (the branch that may fall back to
TEXT_GENERATION), ensure you do NOT attach or send the image_url part when the
chosen service/model is not multimodal (check the selected service/model
capability before adding image parts) and align the public is_available()
implementation to return false unless a multimodal-capable builder/service is
available so the feature isn't advertised when grounding will be downgraded
(reference methods: with_file, generate_text, parse_suggestions, is_available
and the TEXT_GENERATION fallback).

---

Outside diff comments:
In `@includes/classes/AI/AltTextGenerator.php`:
- Around line 35-36: is_available() currently only checks for installed helper
functions and so advertises the Alt Text UI even when no AI provider/model is
configured; change it to return true only when the same provider/configuration
checks used by the generation path indicate a usable service. Concretely, update
AI/AltTextGenerator::is_available() to call the same configuration/provider
helper(s) used by the POST-generation flow (the code path that currently returns
"no_configured_service") — e.g. check the ai_services() result for a
configured/active provider or call the existing helper that verifies an AI
service is configured — and only return true when those checks pass (in addition
to any required function_exists checks) so admin/class-enqueue-admin.php's
aiAltAvailable reflects an actually usable service.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5d28082e-a401-436c-b8bb-f0bb3af4c3e4

📥 Commits

Reviewing files that changed from the base of the PR and between dc438f0 and 018fd16.

📒 Files selected for processing (1)
  • includes/classes/AI/AltTextGenerator.php

Comment thread includes/classes/AI/AltTextGenerator.php Outdated
Comment thread includes/classes/AI/AltTextGenerator.php Outdated
claude added 4 commits June 11, 2026 18:20
…ne plugin

WP 7.0 built-in AI connector uses send() as terminate method with with_image(),
returning GenerativeAiResult. The standalone wp-ai-client plugin uses generate_text()
returning a string. Detect which builder variant is present and call the correct
terminate method/text extraction path.

Also adds robust extract_text_from_result() and text_from_candidate() helpers
that handle all three known response shapes (string, ai-services Candidates object
with ->get(), WP 7.0 GenerativeAiResult with ->get_candidates() array).

https://claude.ai/code/session_01HkDypCgHrgKqBB4cqwnBry
…ctly

The WordPress AI connector's with_file() requires a base64 data URI, not
a plain URL. Previously we were passing a URL and trying to detect the
terminate method via method_exists() before with_file() was called, which
always missed generate_text() since the method may not be present on the
initial builder object.

Now: read the image from disk (or download it) → convert to data URI →
call wp_ai_client_prompt($prompt)->with_file($data_uri)->generate_text()
directly, matching the pattern used by the WordPress/ai plugin itself.

https://claude.ai/code/session_01HkDypCgHrgKqBB4cqwnBry
The full-resolution original can be many MB, causing cURL to time out
before Google's API responds. Try local 'large' → 'medium_large' →
'medium' → 'full' sizes in order, stopping at the first readable file.
Falls back to downloading the 'large' URL if no local file is found.

https://claude.ai/code/session_01HkDypCgHrgKqBB4cqwnBry
Adds a Generate with AI button to the Simplified Summary section of the
Readability Analysis panel in the block editor sidebar. Clicking generates
a plain-language summary of the post content at or below 8th-grade reading
level (WCAG 3.1.5 AAA) using the WordPress AI connector or AI Services plugin.

- SimplifiedSummaryGenerator.php: text-only AI generation, processes post
  content through the same filters used by the readability grade calculator,
  truncates to 8000 chars to keep latency low
- REST endpoint POST /accessibility-checker/v1/generate-simplified-summary
- Generate with AI button appears above the textarea when aiAltAvailable;
  pre-fills the textarea so the user can review before clicking Save Summary

https://claude.ai/code/session_01HkDypCgHrgKqBB4cqwnBry
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants