Skip to content
Open
8 changes: 7 additions & 1 deletion Api/RequestInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
interface RequestInterface
{
/**
* Retrieve products id.
* Retrieve product id.
* @return int
*/
public function getId(): int;
Expand All @@ -17,4 +17,10 @@ public function getId(): int;
* @return bool
*/
public function getOverwrite(): bool;

/**
* Retrieve store id.
* @return int
*/
public function getStoreId(): int;
}
5 changes: 4 additions & 1 deletion Controller/Adminhtml/Product/MassEnrich.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface;
use Magento\Framework\Controller\ResultFactory;
use Magento\Framework\Exception\LocalizedException;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Ui\Component\MassAction\Filter;
use MageOS\CatalogDataAI\Model\Config;
use MageOS\CatalogDataAI\Model\Product\Publisher;
Expand All @@ -27,6 +28,7 @@ public function __construct(
private readonly Config $config,
private readonly Publisher $publisher,
private readonly ProductRepositoryInterface $productRepository,
private readonly StoreManagerInterface $storeManager,
) {
parent::__construct($context);
}
Expand All @@ -42,11 +44,12 @@ public function execute(): Redirect
$collection = $this->filter->getCollection($this->collectionFactory->create());

$productEnriched = 0;
$storeId = (int)$this->storeManager->getStore()->getId();
if ($this->config->isEnabled()) {
/** @var Product $product */
foreach ($collection->getItems() as $product) {
//@TODO: we hit rate limit, change to batching the request
$this->publisher->execute($product->getId(), $this->overwrite);
$this->publisher->execute($product->getId(), $this->overwrite, $storeId);
$productEnriched++;
}

Expand Down
37 changes: 34 additions & 3 deletions Model/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ class Config

private array $attributePromptsMap = [];

private const LOCALE_LANGUAGE_MAP = [
'af' => 'Afrikaans', 'ar' => 'Arabic', 'bg' => 'Bulgarian', 'bn' => 'Bengali',
'ca' => 'Catalan', 'cs' => 'Czech', 'cy' => 'Welsh', 'da' => 'Danish',
'de' => 'German', 'el' => 'Greek', 'en' => 'English', 'es' => 'Spanish',
'et' => 'Estonian', 'fa' => 'Persian', 'fi' => 'Finnish', 'fr' => 'French',
'gl' => 'Galician', 'he' => 'Hebrew', 'hi' => 'Hindi', 'hr' => 'Croatian',
'hu' => 'Hungarian', 'id' => 'Indonesian', 'it' => 'Italian', 'ja' => 'Japanese',
'ka' => 'Georgian', 'ko' => 'Korean', 'lt' => 'Lithuanian', 'lv' => 'Latvian',
'mk' => 'Macedonian', 'ms' => 'Malay', 'nb' => 'Norwegian', 'nl' => 'Dutch',
'pl' => 'Polish', 'pt' => 'Portuguese', 'ro' => 'Romanian', 'ru' => 'Russian',
'sk' => 'Slovak', 'sl' => 'Slovenian', 'sq' => 'Albanian', 'sr' => 'Serbian',
'sv' => 'Swedish', 'th' => 'Thai', 'tr' => 'Turkish', 'uk' => 'Ukrainian',
'vi' => 'Vietnamese', 'zh' => 'Chinese',
];

public function __construct(
private readonly ScopeConfigInterface $scopeConfig,
private readonly Json $json
Expand Down Expand Up @@ -109,11 +124,27 @@ public function canEnrich(Product $product): bool
return $this->isEnabled() && $this->getApiKey() && $product->isObjectNew();
}

public function getSystemPrompt(): mixed
public function getSystemPrompt(): string
{
return $this->scopeConfig->getValue(
self::XML_PATH_OPENAI_API_ADVANCED_SYSTEM_PROMPT
$basePrompt = (string)$this->scopeConfig->getValue(
self::XML_PATH_OPENAI_API_ADVANCED_SYSTEM_PROMPT,
\Magento\Store\Model\ScopeInterface::SCOPE_STORE
);

$locale = $this->scopeConfig->getValue(
'general/locale/code',
\Magento\Store\Model\ScopeInterface::SCOPE_STORE
);

if ($locale) {
$langCode = substr($locale, 0, 2);
$language = self::LOCALE_LANGUAGE_MAP[$langCode] ?? null;
if ($language && $langCode !== 'en') {
$basePrompt = 'Respond in ' . $language . '. ' . $basePrompt;
}
}

return $basePrompt;
}

public function getTemperature(): float
Expand Down
21 changes: 8 additions & 13 deletions Model/Product/Consumer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,28 @@
use Magento\Catalog\Model\ProductRepository;
use Magento\Store\Model\StoreManagerInterface;

/**
* Class Consumer
* @package Gaiterjones\RabbitMQ\MessageQueues\Product
*/
class Consumer
{
/**
* Consumer constructor.
*/
public function __construct(
private readonly Enricher $enricher,
private readonly ProductRepository $productRepository,
private readonly Enricher $enricher,
private readonly ProductRepository $productRepository,
private readonly StoreManagerInterface $storeManager
) {
}

public function execute(Request $request): void
{
// @TODO: enrich for all stores if different value or language
$this->storeManager->setCurrentStore(0);
$product = $this->productRepository->getById($request->getId());
$this->storeManager->setCurrentStore($request->getStoreId());
$product = $this->productRepository->getById(
$request->getId(),
false,
$request->getStoreId()
);
$product->setData('mageos_catalogai_overwrite', $request->getOverwrite());
$this->enricher->execute($product);

if ($product->hasDataChanges()) {
$this->productRepository->save($product);
}
}

}
7 changes: 2 additions & 5 deletions Model/Product/Publisher.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,12 @@ public function __construct(
) {
}

/**
* @param int|string $productId
* @param bool $overwrite
*/
public function execute(int|string $productId, bool $overwrite = false): void
public function execute(int|string $productId, bool $overwrite = false, int $storeId = 0): void
{
$request = $this->requestFactory->create([
'id' => (int)$productId,
'overwrite' => $overwrite,
'storeId' => $storeId,
]);
$this->publisher->publish(self::TOPIC_NAME, $request);
}
Expand Down
14 changes: 7 additions & 7 deletions Model/Product/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,23 @@ class Request implements RequestInterface
{
public function __construct(
private readonly int $id,
private readonly bool $overwrite
private readonly bool $overwrite,
private readonly int $storeId = 0
) {
}

/**
* @inheritDoc
*/
public function getId(): int
{
return $this->id;
}

/**
* @inheritDoc
*/
public function getOverwrite(): bool
{
return $this->overwrite;
}

public function getStoreId(): int
{
return $this->storeId;
}
}
6 changes: 5 additions & 1 deletion Observer/Product/SaveAfter.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ public function execute(Observer $observer): void
$this->persistDeferredEnrichments($product);

if ($this->config->canEnrich($product) && $this->config->isAsync()) {
$this->publisher->execute($product->getId(), false);
$this->publisher->execute(
$product->getId(),
false,
(int)$product->getStoreId()
);
}
}

Expand Down
45 changes: 41 additions & 4 deletions Test/Unit/Model/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,51 @@ public function testGetApiModelReturnsEmptyStringWhenNull(): void

public function testGetSystemPrompt(): void
{
$this->scopeConfig->expects($this->once())
->method('getValue')
->with(Config::XML_PATH_OPENAI_API_ADVANCED_SYSTEM_PROMPT)
->willReturn('You are a helpful assistant');
$this->scopeConfig->method('getValue')
->willReturnMap([
[Config::XML_PATH_OPENAI_API_ADVANCED_SYSTEM_PROMPT, ScopeInterface::SCOPE_STORE, null, 'You are a helpful assistant'],
['general/locale/code', ScopeInterface::SCOPE_STORE, null, 'en_US'],
]);

$this->assertSame('You are a helpful assistant', $this->config->getSystemPrompt());
}

public function test_get_system_prompt_prepends_language_for_non_english_locale(): void
{
$this->scopeConfig->method('getValue')
->willReturnMap([
[Config::XML_PATH_OPENAI_API_ADVANCED_SYSTEM_PROMPT, ScopeInterface::SCOPE_STORE, null, 'Be a content generator.'],
['general/locale/code', ScopeInterface::SCOPE_STORE, null, 'fr_FR'],
]);

$result = $this->config->getSystemPrompt();

$this->assertStringContainsString('Respond in French', $result);
$this->assertStringContainsString('Be a content generator.', $result);
}

public function test_get_system_prompt_skips_language_prefix_for_english_locale(): void
{
$this->scopeConfig->method('getValue')
->willReturnMap([
[Config::XML_PATH_OPENAI_API_ADVANCED_SYSTEM_PROMPT, ScopeInterface::SCOPE_STORE, null, 'Be a content generator.'],
['general/locale/code', ScopeInterface::SCOPE_STORE, null, 'en_US'],
]);

$this->assertSame('Be a content generator.', $this->config->getSystemPrompt());
}

public function test_get_system_prompt_handles_unknown_locale_gracefully(): void
{
$this->scopeConfig->method('getValue')
->willReturnMap([
[Config::XML_PATH_OPENAI_API_ADVANCED_SYSTEM_PROMPT, ScopeInterface::SCOPE_STORE, null, 'Be a content generator.'],
['general/locale/code', ScopeInterface::SCOPE_STORE, null, 'xx_YY'],
]);

$this->assertSame('Be a content generator.', $this->config->getSystemPrompt());
}

public function testGetApiMaxTokens(): void
{
$this->scopeConfig->expects($this->once())
Expand Down
3 changes: 3 additions & 0 deletions Test/Unit/Model/Product/PublisherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public function testExecuteCreatesRequestWithCorrectParams(): void
->with([
'id' => $productId,
'overwrite' => $overwrite,
'storeId' => 0,
])
->willReturn($request);

Expand Down Expand Up @@ -80,6 +81,7 @@ public function testExecuteDefaultOverwriteIsFalse(): void
->with([
'id' => $productId,
'overwrite' => false,
'storeId' => 0,
])
->willReturn($request);

Expand All @@ -102,6 +104,7 @@ public function testExecuteCastsStringIdToInt(): void
->with([
'id' => 42,
'overwrite' => $overwrite,
'storeId' => 0,
])
->willReturn($request);

Expand Down
18 changes: 17 additions & 1 deletion Test/Unit/Model/Product/RequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use MageOS\CatalogDataAI\Model\Product\Request;
use PHPUnit\Framework\TestCase;

class RequestTest extends TestCase
final class RequestTest extends TestCase
{
public function testGetIdReturnsConstructorValue(): void
{
Expand All @@ -30,4 +30,20 @@ public function testGetOverwriteReturnsFalse(): void
$request = new Request(1, false);
$this->assertFalse($request->getOverwrite());
}

public function test_request_carries_store_id(): void
{
$request = new Request(42, true, 3);

$this->assertSame(42, $request->getId());
$this->assertTrue($request->getOverwrite());
$this->assertSame(3, $request->getStoreId());
}

public function test_store_id_defaults_to_zero(): void
{
$request = new Request(42, false);

$this->assertSame(0, $request->getStoreId());
}
}
1 change: 1 addition & 0 deletions etc/module.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<module name="MageOS_CatalogDataAI">
<sequence>
<module name="Magento_Catalog"/>
<module name="Magento_Store"/>
</sequence>
</module>
</config>
Loading