Prompt template extension points - #770
Conversation
|
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 If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
…emplate-extension-points
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #770 +/- ##
=============================================
+ Coverage 78.44% 79.68% +1.24%
- Complexity 2454 2460 +6
=============================================
Files 104 104
Lines 9925 9955 +30
=============================================
+ Hits 7786 7933 +147
+ Misses 2139 2022 -117
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
dkotter
left a comment
There was a problem hiding this comment.
While the changes in this PR work, I think it may be worth discussing or exploring an alternative approach. Right now each individual Ability is responsible for ensuring content is passed through a filter. So as new Abilities are added, we'll want to ensure they get these same filters.
I'm wondering if it would be better / easier to maintain if these filters were automatically added. As an example, if we introduce a get_prompt_builder method on the Abstract_Ability class, the filter could live there and get automatically applied when each individual Ability uses that method. That would reduce the amount of code changes in this PR and would ensure any new Abilities in the future automatically get those filters
|
Hi @dkotter I looked into moving
If Proposed Compromise We could introduce a protected function filter_prompt_builder( \WP_AI_Client_Prompt_Builder $builder, string $feature_class, array $fallback_models = array(), ...$filter_args ) {
// 1. Apply model preferences
$builder = $this->set_provider_model_preference( $builder, $feature_class, $fallback_models );
// 2. Automatically apply the builder filter
return apply_filters( "wpai_{$this->get_ability_slug()}_prompt_builder", $builder, ...$filter_args );
}Then, an individual ability simply configures its specific settings and passes the builder through the helper: $prompt_builder = wp_ai_client_prompt( $prompt )
->using_system_instruction( $this->get_system_instruction() )
->using_temperature( 0.7 );
// One standardized call that applies the model fallbacks AND fires the filter hook
$prompt_builder = $this->filter_prompt_builder( $prompt_builder, Title_Generation_Experiment::class, array(), $prompt );We could do something similar for the prompt string itself with a This drastically reduces the boilerplate across the abilities and centralizes the hook logic. While new abilities will still have to call Let me know if this approach sounds good to you, and I'll update the PR! |
|
That's a decently thought out approach and I'm not opposed to it. One other solution to consider after looking at things again is to add this filter in the existing So I'd suggest still adding a new private function filter_prompt_builder( $prompt_builder ) {
$ability_slug = $this->get_ability_slug();
$filtered = apply_filters( "wpai_prompt_builder_{$ability_slug}", $prompt_builder, $this );
// Guard against a filter returning a non-builder (null, WP_Error, etc.).
if ( ! $filtered instanceof \WP_AI_Client_Prompt_Builder ) {
return $prompt_builder;
}
return $filtered;
}
protected function ensure_text_generation_supported( $prompt_builder, string $message ) {
$prompt_builder = $this->filter_prompt_builder( $prompt_builder );
if ( ! $prompt_builder->is_supported_for_text_generation() ) {
return new WP_Error( 'unsupported_model', $message );
}
return $prompt_builder;
} |
|
@dkotter However, I'd lean toward keeping a dedicated 1. Loss of Explicit Context Variables This allows a third-party developer to easily check if the requested summary is 'short' or 'long' and adjust the builder accordingly. If we bury the filter inside While we could pass 2. Separation of Concerns Since new abilities will have to call Let me know what you think. |
|
@the-hercules This all sounds like valid arguments to me so happy to review and test things once changes are up. Thanks! |
- Add ability-specific filters for system instructions, prompts, and prompt builders - Refactor to process dynamic hooks - Apply new extension points across all existing AI abilities - Add documentation and integration tests
|
@dkotter I’ve committed the changes you requested. Could you please review them when you get a chance? |
Add prompt template extension points
Summary
Introduces consistent, scoped extension points so developers can customize the prompts
and model configuration of AI abilities without forking the plugin. Adds three
per-ability filter hooks plus a developer guide.
Closes #192
What changed
A single slug helper on
Abstract_Abilityderives a hook-safe slug from each abilityname (
ai/title-generation→title_generation), and three filters are wired throughevery AI ability:
wpai_{slug}_system_instruction— override/extend the system instruction for oneability. Added once in
Abstract_Ability::get_system_instruction(), running afterthe existing global
wpai_system_instructionfilter.wpai_{slug}_prompt— modify the assembled user prompt before it is sent. Added tothe 10 abilities that did not already expose one.
wpai_{slug}_prompt_builder— adjust temperature, model/provider preference, requestoptions, etc. Added to all 12 prompt-building abilities, after the default model
preference is applied and before generation support is verified.
Abilities covered: Title, Excerpt, Summarization, Content Resizing, Meta Description,
Editorial Updates, Editorial Notes, Alt Text, Image Prompt Generation, Image Generation,
Comment Analysis, Content Classification.
Where abilities carry extra context, it is passed as additional filter arguments
(e.g.
$lengthfor summarization,$block_typefor editorial notes,$actionfor contentresizing).
Backward compatibility
wpai_system_instructionfilter is unchanged; the new scoped filter runs after it.meta_descriptionandcontent_classificationalready shipped awpai_{slug}_promptfilter with richer signatures (
$content/$title, taxonomy args). Those are left exactlyas-is — no duplicate hook is registered — so existing integrations keep working. They are
documented alongside the new hooks.
wpai_meta_description_result_temperaturestill applies; the new meta-description builderfilter runs after it.
byte-for-byte unchanged.
Design notes / decisions
A few decisions were made in the absence of issue feedback (happy to adjust):
instruction control; the global filter alone would require name-branching.
_promptis the image prompt (not postcontent), and editorial guidelines are appended after the filter — documented as such.
set_provider_model_preference()so it can intentionallyoverride the developer-mode model choice without breaking the default path.
comment_analysis,content_classification,editorial_notes) document that the builder should be extended, not replaced, to preserveJSON response schemas.