Skip to content

Conversation

@caseymanos
Copy link
Contributor

@caseymanos caseymanos commented Nov 26, 2025

What?

Closes #26

Implements a centralized AI service layer (AI_Service) that provides a consistent interface for experimental features to communicate with AI providers through the WP AI Client SDK.

Why?

The plugin needs a unified service layer so that experimental features can communicate with AI providers without duplicating logic or directly coupling to the AI_Client SDK. This provides:

  • A single point of configuration for AI provider requests
  • Graceful handling when no provider is configured
  • Hooks and filters for developers/hosts to customize behavior
  • A foundation for future AI-powered features

How?

New files:

  • includes/Services/AI_Service.php - Singleton service class with generate_text(), generate_texts(), create_prompt(), is_available() methods
  • includes/Services/Contracts/AI_Service_Interface.php - Interface contract ensuring consistent implementation
  • tests/Integration/Includes/Services/AI_ServiceTest.php - Integration tests (26 test cases)

Modified files:

  • includes/bootstrap.php - Initializes AI_Service after AI_Client::init()
  • includes/helpers.php - Adds get_ai_service() helper function

Hooks and filters:

Hook Type Description
ai_service_available Filter Override provider availability detection
ai_service_initialized Action Fires when AI service initializes
ai_service_prompt_builder Filter Modify prompt builder before generation

Testing Instructions

  1. Ensure you have an AI provider configured (Settings > AI Credentials)
  2. Run automated tests: npm run test:php
  3. Verify the service via WP-CLI:
npm run wp-env -- run cli wp eval '
$service = WordPress\AI\get_ai_service();
echo "Available: " . ($service->is_available() ? "yes" : "no") . "\n";
if ($service->is_available()) {
    $result = $service->generate_text("Say hello in 3 words");
    echo "Result: " . $result . "\n";
}
'
  1. Test without credentials by temporarily removing them - is_available() should return false
  2. Verify hooks work by adding a filter:
add_filter( 'ai_service_available', '__return_false' );
// Service should now report unavailable

Testing Instructions for Keyboard

Not applicable - this PR adds backend service infrastructure only with no UI changes.

Screenshots or screencast

Not applicable - this PR adds backend service infrastructure only with no UI changes.

Open WordPress Playground Preview

Implements a service layer that provides a consistent interface for
experimental features to communicate with AI providers through the
WP AI Client SDK.

New files:
- includes/Services/AI_Service.php - Singleton service class
- includes/Services/Contracts/AI_Service_Interface.php - Interface contract
- tests/Integration/Includes/Services/AI_ServiceTest.php - Integration tests

Features:
- Centralized AI provider configuration and request handling
- Model preference caching with clear_model_cache() method
- Graceful handling when no provider is configured
- Helper function get_ai_service() for easy access

Hooks and filters:
- ai_service_available: Override provider availability detection
- ai_service_initialized: Action when service initializes
- ai_service_prompt_builder: Filter prompt builder before generation

Closes WordPress#26
@github-actions
Copy link

github-actions bot commented Nov 26, 2025

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @iTsphillgood.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: iTsphillgood.

Co-authored-by: caseymanos <[email protected]>
Co-authored-by: dkotter <[email protected]>
Co-authored-by: felixarntz <[email protected]>
Co-authored-by: jeffpaul <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@codecov
Copy link

codecov bot commented Nov 26, 2025

Codecov Report

❌ Patch coverage is 88.88889% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 50.91%. Comparing base (1c00879) to head (9a4845d).
⚠️ Report is 35 commits behind head on develop.

Files with missing lines Patch % Lines
...bilities/Excerpt_Generation/Excerpt_Generation.php 0.00% 1 Missing ⚠️
includes/Abilities/Summarization/Summarization.php 0.00% 1 Missing ⚠️
...es/Abilities/Title_Generation/Title_Generation.php 0.00% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             develop     #101      +/-   ##
=============================================
+ Coverage      50.33%   50.91%   +0.57%     
- Complexity       366      375       +9     
=============================================
  Files             26       27       +1     
  Lines           1951     1974      +23     
=============================================
+ Hits             982     1005      +23     
  Misses           969      969              
Flag Coverage Δ
unit 50.91% <88.88%> (+0.57%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jeffpaul
Copy link
Member

@caseymanos thanks for the PR! I see you note this closes #26, but looking to double confirm that the various goals in that issue description are all covered by your work here; correct?

Copy link
Member

@felixarntz felixarntz left a comment

Choose a reason for hiding this comment

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

While overall I agree that a more opinionated AI service makes sense in the context of this plugin, I think it does too many things. Specifically, it obscures some of the flexibility that the underlying WordPress AI Client APIs provide, for no good reason as far as I can tell.

I think we should simplify the implementation to focus on creating a prompt builder with plugin specific defaults applied, and leave the rest to the regular prompt builder APIs.

@iTsphillgood
Copy link

While overall I agree that a more opinionated AI service makes sense in the context of this plugin, I think it does too many things. Specifically, it obscures some of the flexibility that the underlying WordPress AI Client APIs provide, for no good reason as far as I can tell.

I think we should simplify the implementation to focus on creating a prompt builder with plugin specific defaults applied, and leave the rest to the regular prompt builder APIs.

Have you tried Google Jules agent or Gemini 3 on cli?

@jeffpaul jeffpaul changed the base branch from trunk to develop November 26, 2025 22:08
Address @felixarntz review comments:

- Remove AI_Service_Interface (premature abstraction)
- Remove DEFAULT_TEMPERATURE constant (no forced defaults)
- Remove generate_text/generate_texts methods (redundant wrappers)
- Make create_prompt() the centerpiece with optional $options array
- Use existing get_preferred_models() helper instead of duplicating
- Simplify from 311 to 181 lines

The new API follows Felix's suggested pattern:
$text = $ai_service->create_prompt($prompt, $options)->generate_text();
@caseymanos caseymanos force-pushed the issue-26-ai-connection-manager branch from b3bfd72 to 8e0a728 Compare November 27, 2025 09:26
@caseymanos
Copy link
Contributor Author

@felixarntz @jeffpaul
Thanks for the feedback! I made the following improvements:

  1. Removed the interface - Deleted AI_Service_Interface.php
  2. Removed DEFAULT_TEMPERATURE - No forced defaults; consumers specify explicitly or use provider defaults
  3. Made create_prompt() the centerpiece - Removed generate_text() and generate_texts() wrapper methods

Additional improvements:

  • Simplified options handling using the SDK's ModelConfig::fromArray()
  • Options array supports all 11 SDK scalar options with snake_case keys
  • Full SDK API remains available via method chaining after create_prompt()

Updated API

// Simple usage - model preferences auto-applied
$text = get_ai_service()->create_prompt('Summarize this...')->generate_text();

// With options array
$text = get_ai_service()->create_prompt('Translate to French', [
    'system_instruction' => 'You are a translator.',
    'temperature'        => 0.3,
])->generate_text();

// Full SDK access via chaining
$titles = get_ai_service()->create_prompt('Generate titles')
    ->using_candidate_count(5)
    ->generate_texts();

Hooks Available

| Hook                   | Type   | Description                              |
|------------------------|--------|------------------------------------------|
| ai_service_available   | Filter | Override provider availability detection |
| ai_service_initialized | Action | Fires when AI service initializes        |
| ai_preferred_models    | Filter | Customize default model preferences      |

Note: Removed ai_service_prompt_builder filter as it's no longer needed - create_prompt() returns the builder directly so consumers can modify it with the SDK

@jeffpaul jeffpaul requested a review from felixarntz November 28, 2025 15:54
Copy link
Member

@felixarntz felixarntz left a comment

Choose a reason for hiding this comment

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

@caseymanos Almost LGTM, just one remaining concern.

@felixarntz
Copy link
Member

Related: @dkotter @caseymanos I'm not sure about the get_preferred_models() function, only spotted it now. Didn't want to flag it during the review since it's already in the codebase. Can we rename this to something like get_preferred_models_for_text_generation()? Because that's closer to what that function does, based on the models it currently returns.

Address Felix's review feedback:
- Remove is_available() method from AI_Service (SDK provides
  is_supported_for_*() methods on Prompt_Builder for capability checking)
- Rename get_preferred_models() to get_preferred_models_for_text_generation()
  to clarify the function returns text-generation-specific models
- Rename filter from ai_preferred_models to
  ai_preferred_models_for_text_generation
- Update docstring example to use SDK's is_supported_for_text_generation()
@caseymanos
Copy link
Contributor Author

@felixarntz
Addressed both comments - removed is_available() entirely, which also gets rid of the hardcoded
option string. Updated the docstring example to show using the SDK's
is_supported_for_text_generation() instead.

Also renamed get_preferred_models() to get_preferred_models_for_text_generation() (filter renamed too).

@jeffpaul jeffpaul requested a review from felixarntz December 1, 2025 03:25
felixarntz
felixarntz previously approved these changes Dec 1, 2025
Copy link
Member

@felixarntz felixarntz left a comment

Choose a reason for hiding this comment

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

@caseymanos Thanks, LGTM!

Note there are some merge conflicts though that need to be resolved.

Address dkotter and felixarntz review feedback:
- Rename create_prompt() to create_textgen_prompt() to clarify
  this method is specifically for text generation
- Update @SInCE tags from 0.1.0 to x.x.x for release process
- Rename hook from ai_service_initialized to
  ai_experiments_service_initialized to use correct prefix
@caseymanos
Copy link
Contributor Author

caseymanos commented Dec 2, 2025

@felixarntz @dkotter

  • Renamed create_prompt() to create_textgen_prompt() to make it clear this is specifically for text
    generation, and gives space for future image/audio prompts
  • Updated all @ since tags to x.x.x for the release process
  • Renamed hook from ai_service_initialized to ai_experiments_service_initialized to use the correct prefix

@jeffpaul jeffpaul modified the milestones: 0.1.1, 0.2.0 Dec 2, 2025
dkotter
dkotter previously approved these changes Dec 2, 2025
@dkotter
Copy link
Collaborator

dkotter commented Jan 13, 2026

@caseymanos Thanks for the work here. We've merged in a few other PRs that used the get_preferred_models function and since this PR changes the name of that, we now have a few linting errors. Do you have time to update those references?

Comment on lines 223 to 224
// Initialize the WP AI Client.
AI_Client::init();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looking at this again, we already run this exact same code a few lines above (currently line 206). Is there a reason to initialize it again here? Or can this be removed?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Note I removed that here 9a4845d but let me know if that is wrong

Comment on lines 227 to 228
$ai_service = AI_Service::get_instance();
$ai_service->init();
Copy link
Collaborator

@dkotter dkotter Jan 14, 2026

Choose a reason for hiding this comment

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

I guess question on this as well looking at it again. It seems the init method we are calling here doesn't actually do anything (beyond just running a hook). Do we need this init method and the loading of this class here? Or can we remove this and anyone that wants to use this class can be done via the get_ai_service helper function?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Note I removed these lines and the init method here: 9a4845d. But let me know if that will cause problems

@jeffpaul jeffpaul mentioned this pull request Jan 16, 2026
20 tasks
@jeffpaul jeffpaul modified the milestones: 0.2.0, 0.3.0 Jan 20, 2026
@jeffpaul jeffpaul merged commit 0412047 into WordPress:develop Jan 23, 2026
28 checks passed
@dkotter dkotter modified the milestones: 0.3.0, 0.2.1 Jan 26, 2026
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.

Integrate WP AI Client SDK with a simple Service Provider setup

5 participants