Skip to content

Commit

Permalink
add output of estimated price of data
Browse files Browse the repository at this point in the history
  • Loading branch information
boxblinkracer committed Dec 27, 2024
1 parent f52d910 commit 05c6c6d
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 50 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ using the [Keep a CHANGELOG](https://keepachangelog.com/) principles.
- Add brand new option to generate **product variants**. Configure what property group to use, and AI will automatically generate all variants, if appropriate for the product.
- Add 2 files in the cache directory for the generated prompt and response of product generation requests.
- Add new product image styles. Open the plugin configuration an select what styles to use for the product images.
- Add new outout of **estimated price** for the generated data.

### Changed

Expand Down
5 changes: 5 additions & 0 deletions src/Command/MediaGenerateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use AIDemoData\Service\Config\ConfigService;
use AIDemoData\Service\Generator\MediaGenerator;
use AIDemoData\Service\Generator\MediaGeneratorInterface;
use AIDemoData\Traits\CommandOutputTrait;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
Expand All @@ -13,6 +14,8 @@

class MediaGenerateCommand extends Command implements MediaGeneratorInterface
{
use CommandOutputTrait;

public static $defaultName = 'ai-demodata:generate:media';

/**
Expand Down Expand Up @@ -105,6 +108,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$this->mediaGenerator->generate($keyWords, $size, $count);

$this->showOpenAIUsageData($output);

if ($this->errorCount <= 0) {
$this->io->success('Generated ' . $this->generatedCount . ' images for keywords in CMS Media folder');
} else {
Expand Down
7 changes: 6 additions & 1 deletion src/Command/ProductGenerateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use AIDemoData\Service\Config\ConfigService;
use AIDemoData\Service\Generator\ProductGenerator;
use AIDemoData\Service\Generator\ProductGeneratorInterface;
use AIDemoData\Traits\CommandOutputTrait;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
Expand All @@ -15,6 +16,8 @@

class ProductGenerateCommand extends Command implements ProductGeneratorInterface
{
use CommandOutputTrait;

public static $defaultName = 'ai-demodata:generate:products';

/**
Expand Down Expand Up @@ -150,7 +153,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$rows[] = ['Image Size', $imgSize];



$table = new Table($output);
$table->setStyle('default');
$table->setHeaders(['Configuration', 'Value']);
Expand Down Expand Up @@ -178,6 +180,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$imageStyles
);


$this->showOpenAIUsageData($output);

if ($this->errorCount <= 0) {
$this->io->success('Generated ' . $this->generatedCount . ' products for keywords');
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/Service/Generator/ProductGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ public function generate(string $keywords, int $maxCount, string $category, stri
$prompt .= 'Every resulting line should be in the order and sort provided below:' . PHP_EOL;
$prompt .= PHP_EOL;
$prompt .= '- product count.' . PHP_EOL;
$prompt .= '- product number code. should be 24 unique random alphanumeric (always unique, maybe consider datetime now).' . PHP_EOL;
$prompt .= '- product number code. should be 24 unique random alphanumeric' . PHP_EOL;
$prompt .= '- name of the product.' . PHP_EOL;
$prompt .= '- description (about ' . $descriptionLength . ' characters).' . PHP_EOL;
$prompt .= '- price value (no currency just number).' . PHP_EOL;
Expand Down
79 changes: 31 additions & 48 deletions src/Service/OpenAI/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ public function generateText(string $prompt): Choice

$this->openAi = new OpenAi($this->apiKey);

$model = 'gpt-3.5-turbo-instruct';

$params = [
'model' => "gpt-3.5-turbo-instruct",
'model' => $model,
'prompt' => $prompt,
'temperature' => 0.3,
'max_tokens' => 1000,
Expand All @@ -65,6 +66,8 @@ public function generateText(string $prompt): Choice
throw new \Exception($msg);
}

$this->trackTextCosts($prompt, $json, $model);

if (!isset($json['choices'])) {
throw new \Exception('No choices found in OpenAI response.');
}
Expand Down Expand Up @@ -100,12 +103,15 @@ public function generateImage(string $prompt, string $size): string

$this->openAi = new OpenAi($this->apiKey);

$model = "dall-e-3";

$complete = $this->openAi->image([
"model" => "dall-e-3",
"model" => $model,
"prompt" => $prompt,
"n" => 1,
"size" => $size,
"style" => "natural",
"quality" => "standard",
"response_format" => "url",
]);

Expand All @@ -120,66 +126,43 @@ public function generateImage(string $prompt, string $size): string
throw new \Exception($msg);
}

if ($size === "1024x1024") {
OpenAIUsageTracker::getInstance()->addRequest($prompt, 0.040);
} else {
OpenAIUsageTracker::getInstance()->addRequest($prompt, 0.080);
}

return (string)$json['data'][0]['url'];
}


/**
* @param string $prompt
* @throws \JsonException
* @return Choice
* @param array<mixed> $json
* @param string $model
* @return void
*/
public function askChatGPT(string $prompt): Choice
private function trackTextCosts(string $prompt, array $json, string $model): void
{
if (empty($this->apiKey)) {
throw new \Exception('No API Key found in plugin configuration. Please provide your key');
if (!isset($json['usage'])) {
OpenAIUsageTracker::getInstance()->addMissingPrices($prompt, $model);
return;
}

$this->openAi = new OpenAi($this->apiKey);
$pricingData = json_decode((string)file_get_contents(__DIR__ . '/pricing.json'), true);

$params = [
'model' => "gpt-3.5-turbo-instruct",
'messages' => [
['role' => 'user', 'content' => $prompt],
],
'temperature' => 0.8,
'max_tokens' => 400,
'top_p' => 1.0,
'frequency_penalty' => 0.0,
'presence_penalty' => 0.0,
];


$complete = (string)$this->openAi->chat($params);

$json = json_decode($complete, true, 512, JSON_THROW_ON_ERROR);

if (!is_array($json)) {
return new Choice('');
}

if (isset($json['error'])) {
$msg = 'OpenAI Error: ' . $json['error']['message'] . '[' . $json['error']['code'] . ']';
throw new \Exception($msg);
}

if (!isset($json['choices'])) {
throw new \Exception('No choices found in OpenAI response.');
if (!isset($pricingData[$model])) {
OpenAIUsageTracker::getInstance()->addMissingPrices($prompt, $model);
}

$choices = $json['choices'];

if (!is_array($choices) || count($choices) <= 0) {
return new Choice('');
}
$costsInputToken = $pricingData[$model]['input'] ?? 0;
$costsOutputToken = $pricingData[$model]['output'] ?? 0;

if (!isset($choices[0]['message']['content'])) {
return new Choice('');
}
$totalInputTokens = $json['usage']['prompt_tokens'];
$totalOutputTokens = $json['usage']['completion_tokens'];

$choiceData = $choices[0];
$costUSD = ($totalInputTokens * $costsInputToken) + ($totalOutputTokens * $costsOutputToken);

$text = trim($choiceData['message']['content']);

return new Choice($text);
OpenAIUsageTracker::getInstance()->addRequest($prompt, $costUSD);
}
}
73 changes: 73 additions & 0 deletions src/Service/OpenAI/OpenAIUsageTracker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

namespace AIDemoData\Service\OpenAI;

class OpenAIUsageTracker
{
private static ?OpenAIUsageTracker $instance = null;

/**
* @var array<mixed>
*/
private array $requests = [];

/**
* @var array<mixed>
*/
private array $missingModelPrices = [];


public static function getInstance(): OpenAIUsageTracker
{
if (!self::$instance instanceof OpenAIUsageTracker) {
self::$instance = new self();
}

return self::$instance;
}


public function addRequest(string $prompt, float $costUSD): void
{
$this->requests[] = [
'prompt' => $prompt,
'costUSD' => $costUSD,
];
}

public function addMissingPrices(string $prompt, string $model): void
{
$this->requests[] = [
'prompt' => $prompt,
'costUSD' => 0,
];

$this->missingModelPrices[] = $model;
}

public function getRequestCount(): int
{
return count($this->requests);
}

/**
* Gets the total costs rounded to 2 decimal places
*/
public function getTotalCostsUSD(): float
{
$totalCostsUSD = 0;
foreach ($this->requests as $cost) {
$totalCostsUSD += $cost['costUSD'];
}

return $totalCostsUSD;
}

/**
* @return mixed[]
*/
public function getMissingModelPrices(): array
{
return $this->missingModelPrices;
}
}
18 changes: 18 additions & 0 deletions src/Service/OpenAI/pricing.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"gpt-4": {
"input": 0.00003,
"output": 0.00006
},
"gpt-4-turbo": {
"input": 0.00001,
"output": 0.00003
},
"gpt-3.5-turbo": {
"input": 0.0000015,
"output": 0.000002
},
"gpt-3.5-turbo-instruct": {
"input": 0.0000015,
"output": 0.000002
}
}
22 changes: 22 additions & 0 deletions src/Traits/CommandOutputTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace AIDemoData\Traits;

use AIDemoData\Service\OpenAI\OpenAIUsageTracker;
use Symfony\Component\Console\Output\OutputInterface;

trait CommandOutputTrait
{
protected function showOpenAIUsageData(OutputInterface $output): void
{
$tracker = OpenAIUsageTracker::getInstance();

$output->writeln("\n=== OpenAI Usage Summary =====================");
$output->writeln("Total Requests: " . $tracker->getRequestCount());
$output->writeln("Estimated Costs: " . $tracker->getTotalCostsUSD() . " USD (approx.)");
if (count($tracker->getMissingModelPrices()) > 0) {
$output->writeln("Missing Model Prices: " . implode(', ', $tracker->getMissingModelPrices()));
}
$output->writeln("==============================================\n");
}
}

0 comments on commit 05c6c6d

Please sign in to comment.