Developer guide
How lists are built, and how to get them in code, change the ranking, add providers and override templates.
Architecture
| Free | Pro | |
|---|---|---|
| Namespace | Siteryn_AI_Product_Recommendations | Siteryn_AI_Product_Recommendations_Pro |
| Boot | plugins_loaded, then siteryn_ai_product_recommendations_loaded | On that action, then …_pro_loaded |
| Settings option | siteryn_ai_product_recommendations_settings | …_pro_settings (Merchandising); placement keys live in Free's option |
The pipeline
- Cache: transient
siteryn_aipr_res_*(60 minutes), keyed by placement, sources, limit, product changes, ranking state and language. - Candidates: up-sells, cross-sells, related products, best sellers (pool of 20–60), filtered by
…_candidates. - Strategy:
HybridStrategy(approved AI picks first, rules fill) orRuleBasedStrategy, via…_strategy. - Results:
…_results, then translation and re-validation of every product, trimmed and cached. - AI, in the background: hybrid mode queues
siteryn_ai_product_recommendations_generate_ranking(Action Scheduler or WP-Cron) for products that need a ranking. The provider is never called during a page view.
Getting recommendations in code
use Siteryn_AI_Product_Recommendations\Recommendations\{Context, Engine};
use Siteryn_AI_Product_Recommendations\Frontend\Renderer;
$context = new Context( 'my_widget', array( 123 ), array(), 6 ); // placement, source IDs, exclude IDs, limit
$ids = Engine::recommend( $context ); // int[], best first
$html = Renderer::render( $context, array( 'title' => 'Pairs well with', 'columns' => 3, 'heading' => 'h3' ) );
Call after init. In hybrid mode this can queue an AI ranking for the source product; opt out with the schedule_ranking filter.
Template tag
<?php
if ( function_exists( 'siteryn_ai_product_recommendations' ) ) {
siteryn_ai_product_recommendations( array(
'product_id' => get_the_ID(), // default: current product, else the cart
'limit' => 4, // 1–12
'columns' => 4,
'title' => '', // '' = no heading, omit for the settings heading
'heading' => 'h2',
'title_align' => 'left', // left, center, right; omit for the product page alignment
'class' => 'my-recs',
) );
}
Pass false as the second argument to return the HTML. The shortcode accepts the same attributes except class.
Adding an AI provider
add_action( 'siteryn_ai_product_recommendations_loaded', function () {
class My_Together_Provider extends \Siteryn_AI_Product_Recommendations\AI\Providers\OpenAICompatibleProvider {
public function get_id() { return 'together'; }
public function get_label() { return 'Together AI'; }
public function get_default_model() { return 'meta-llama/Llama-3.3-70B-Instruct-Turbo'; }
public function get_suggested_models() { return array( 'meta-llama/Llama-3.3-70B-Instruct-Turbo' ); }
protected function base_url( \Siteryn_AI_Product_Recommendations\AI\ProviderConfig $config ) {
return 'https://api.together.xyz/v1';
}
}
add_filter( 'siteryn_ai_product_recommendations_providers', function ( $providers ) {
$provider = new My_Together_Provider();
$providers[ $provider->get_id() ] = $provider;
return $providers;
} );
} );
It appears on the AI tab with its own encrypted key. Keys can also be set in wp-config: SITERYN_AI_PRODUCT_RECOMMENDATIONS_{ID}_API_KEY.
A custom ranking strategy
Implement StrategyInterface (get_id(), rank( Context $c, array $ids )) and return it from …_strategy. Wrap the default to keep AI picks:
use Siteryn_AI_Product_Recommendations\Recommendations\{StrategyInterface, Context};
add_filter( 'siteryn_ai_product_recommendations_strategy', function ( $inner, $context ) {
return new class( $inner ) implements StrategyInterface {
private $inner;
public function __construct( $inner ) { $this->inner = $inner; }
public function get_id() { return 'onsale+' . $this->inner->get_id(); }
public function rank( Context $c, array $ids ) {
$ranked = $this->inner->rank( $c, $ids );
usort( $ranked, fn( $a, $b ) => (int) wc_get_product( $b )->is_on_sale() <=> (int) wc_get_product( $a )->is_on_sale() );
return $ranked; // only IDs from $ids
}
};
}, 30, 2 );
Templates
| Template | Variables |
|---|---|
recommendations.php | $products, $title, $heading, $placement, $columns, $settings, $section_id, $class, $layout, $title_align |
product-card.php | $product, $position, $placement, $settings, $heading, $layout |
Pro fbt-bundle.php | $product, $partners, $title, $settings |
Pro personal-placeholder.php | $type, $limit, $exclude |
Copy them to your-theme/siteryn-ai-product-recommendations/ and keep the @version header. Markup hooks for styling and tracking: section.siteryn-aipr--{placement} with data-siteryn-aipr-placement, and article.siteryn-aipr-card with data-product-id and data-position.
Appearance settings are CSS variables on .siteryn-aipr, such as --siteryn-aipr-card-gap, --siteryn-aipr-radius, --siteryn-aipr-heading-size and --siteryn-aipr-sale-color.
JavaScript
- Free adds no storefront script of its own. Card add to cart loads WooCommerce's
wc-add-to-cartscript wherever a list renders, which fires WooCommerce's ownadded_to_cartevent. - Pro's script (
window.siterynAiprPro) fills personal lists, runs sliders, moves checkout lists and handles bundles, once on page load. It fires no custom events, so lists inserted later (AJAX, quick view) aren't filled or turned into sliders.
Endpoints
| Endpoint | Access |
|---|---|
Pro GET /wp-json/siteryn-aipr/v1/recommendations | Public when enabled; see the API |
Pro GET /wp-json/siteryn-aipr/v1/personal | Public; type, ids, exclude, limit, lang → {html} |
Pro AJAX siteryn_aipr_pro_add_bundle | Public, rate-limited |
AJAX siteryn_aipr_product_ranking | manage_woocommerce: generate, approve, clear |
AJAX siteryn_ai_product_recommendations_save_settings · siteryn_ai_product_recommendations_ai_connector (save_key, remove_key, models, test) · admin-post siteryn_ai_product_recommendations_reset_settings; Pro license actions | manage_woocommerce + nonce |
No WP-CLI commands; generate a ranking with wp eval '\Siteryn_AI_Product_Recommendations\Ranking\RankingGenerator::generate( 123 );'.
Data
- Options:
…_settings,…_version,…_credentials(libsodium-encrypted) and…_credential_hosts,…_usage(30 days),…_connectionand…_connections(test results), and the counters…_results_generation,…_cache_generation,…_models_generation. Pro:…_pro_settings,…_pro_fbt_state, license options. - Post meta:
_siteryn_aipr_ai_ranking,_siteryn_aipr_ai_disabled; Pro_siteryn_aipr_pro_pins,_siteryn_aipr_pro_fbt, and order meta_siteryn_aipr_pro_fbt_counted. - Transients
siteryn_aipr_*. No custom tables, no analytics and no custom capabilities.