Developer guide

How lists are built, and how to get them in code, change the ranking, add providers and override templates.

FreePro

Architecture

FreePro
NamespaceSiteryn_AI_Product_RecommendationsSiteryn_AI_Product_Recommendations_Pro
Bootplugins_loaded, then siteryn_ai_product_recommendations_loadedOn that action, then …_pro_loaded
Settings optionsiteryn_ai_product_recommendations_settings…_pro_settings (Merchandising); placement keys live in Free's option

The pipeline

  1. Cache: transient siteryn_aipr_res_* (60 minutes), keyed by placement, sources, limit, product changes, ranking state and language.
  2. Candidates: up-sells, cross-sells, related products, best sellers (pool of 20–60), filtered by …_candidates.
  3. Strategy: HybridStrategy (approved AI picks first, rules fill) or RuleBasedStrategy, via …_strategy.
  4. Results: …_results, then translation and re-validation of every product, trimmed and cached.
  5. 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

TemplateVariables
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

Endpoints

EndpointAccess
Pro GET /wp-json/siteryn-aipr/v1/recommendationsPublic when enabled; see the API
Pro GET /wp-json/siteryn-aipr/v1/personalPublic; type, ids, exclude, limit, lang → {html}
Pro AJAX siteryn_aipr_pro_add_bundlePublic, rate-limited
AJAX siteryn_aipr_product_rankingmanage_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 actionsmanage_woocommerce + nonce

No WP-CLI commands; generate a ranking with wp eval '\Siteryn_AI_Product_Recommendations\Ranking\RankingGenerator::generate( 123 );'.

Data