2026-05-18 07:19:02 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace App\Infrastructure\AI;
|
|
|
|
|
|
|
|
|
|
use App\Domain\AI\Repository\PromptTemplateRepositoryInterface;
|
|
|
|
|
|
|
|
|
|
final class PromptTemplateService
|
|
|
|
|
{
|
|
|
|
|
/** @var array<string, string> */
|
|
|
|
|
private const array DEFAULTS = [
|
|
|
|
|
'specs_research' => <<<'PROMPT'
|
2026-05-18 08:35:52 +00:00
|
|
|
You are a hardware specifications expert. Extract the technical specifications for the {{articleType}}: "{{subject}}".
|
|
|
|
|
|
|
|
|
|
Web search results:
|
|
|
|
|
{{searchResults}}
|
|
|
|
|
|
|
|
|
|
Based on the search results above, list all technical specifications including:
|
|
|
|
|
processor, RAM, storage variants, display size and resolution, GPU, battery capacity,
|
2026-05-18 07:19:02 +00:00
|
|
|
ports, connectivity, weight, dimensions, OS, and any other relevant specs.
|
2026-05-18 08:35:52 +00:00
|
|
|
Be specific and accurate. If a spec is not found in the search results, omit it rather than guessing.
|
2026-05-18 07:19:02 +00:00
|
|
|
PROMPT,
|
|
|
|
|
|
|
|
|
|
'ebay_title' => <<<'PROMPT'
|
|
|
|
|
Create a concise eBay listing title (max 80 characters) for this {{typeName}}.
|
|
|
|
|
Device: {{deviceLabel}}
|
|
|
|
|
Use the most important specifications. Include condition if not "new".
|
|
|
|
|
Condition: {{condition}}
|
|
|
|
|
{{specsSection}}
|
|
|
|
|
Return ONLY the title text, no quotes, no explanation.
|
|
|
|
|
PROMPT,
|
|
|
|
|
|
|
|
|
|
'ebay_description' => <<<'PROMPT'
|
|
|
|
|
Create a professional eBay listing description in German for this {{typeName}}.
|
|
|
|
|
Device: {{deviceLabel}}
|
|
|
|
|
Include all available specifications in a clear, structured format.
|
|
|
|
|
Mention the condition: {{condition}}.
|
|
|
|
|
{{conditionNotes}}
|
|
|
|
|
{{specsSection}}
|
|
|
|
|
Use HTML formatting (ul, li, strong tags). Max 2000 characters.
|
|
|
|
|
PROMPT,
|
|
|
|
|
|
|
|
|
|
'vision_analyze' => <<<'PROMPT'
|
|
|
|
|
Look at this nameplate/label photo of IT hardware.
|
|
|
|
|
Extract the manufacturer (brand), model number/designation, and serial number visible on the label.
|
|
|
|
|
Do not guess or add information not on the label.
|
|
|
|
|
Respond in exactly this format (use empty string if not visible):
|
|
|
|
|
MANUFACTURER: <brand name, e.g. Dell, HP, Lenovo, Medion>
|
|
|
|
|
MODEL: <model number or designation>
|
|
|
|
|
SERIAL: <serial number>
|
|
|
|
|
PROMPT,
|
|
|
|
|
|
|
|
|
|
'json_coding' => <<<'PROMPT'
|
|
|
|
|
Convert the following hardware specifications to a JSON object.
|
|
|
|
|
The JSON must use these exact keys (UUIDs) and follow the indicated value formats:
|
|
|
|
|
|
|
|
|
|
{{schema}}
|
|
|
|
|
{{missingHint}}
|
|
|
|
|
Specifications text:
|
|
|
|
|
{{specsText}}
|
|
|
|
|
|
|
|
|
|
Return ONLY valid JSON. No explanation. No markdown. No extra text.
|
|
|
|
|
JSON:
|
|
|
|
|
PROMPT,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
public function __construct(
|
|
|
|
|
private readonly PromptTemplateRepositoryInterface $repository,
|
|
|
|
|
) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Renders a prompt template by key, substituting {{variable}} placeholders.
|
|
|
|
|
*
|
|
|
|
|
* @param array<string, string> $variables
|
|
|
|
|
*/
|
|
|
|
|
public function render(string $key, array $variables = []): string
|
|
|
|
|
{
|
|
|
|
|
$template = $this->repository->findByKey($key);
|
|
|
|
|
$body = $template?->getBody() ?? self::DEFAULTS[$key]
|
|
|
|
|
?? throw new \InvalidArgumentException("Unknown prompt template key: {$key}");
|
|
|
|
|
|
|
|
|
|
$search = array_map(static fn (string $k) => '{{'.$k.'}}', array_keys($variables));
|
|
|
|
|
|
|
|
|
|
return str_replace($search, array_values($variables), $body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns all known prompt keys with their available variables.
|
|
|
|
|
*
|
|
|
|
|
* @return array<string, list<string>>
|
|
|
|
|
*/
|
|
|
|
|
public static function knownKeys(): array
|
|
|
|
|
{
|
|
|
|
|
return [
|
2026-05-18 08:35:52 +00:00
|
|
|
'specs_research' => ['articleType', 'subject', 'searchResults'],
|
2026-05-18 07:19:02 +00:00
|
|
|
'ebay_title' => ['typeName', 'deviceLabel', 'condition', 'specsSection'],
|
|
|
|
|
'ebay_description' => ['typeName', 'deviceLabel', 'condition', 'conditionNotes', 'specsSection'],
|
|
|
|
|
'vision_analyze' => [],
|
|
|
|
|
'json_coding' => ['schema', 'missingHint', 'specsText'],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @return string */
|
|
|
|
|
public static function defaultFor(string $key): string
|
|
|
|
|
{
|
|
|
|
|
return self::DEFAULTS[$key] ?? '';
|
|
|
|
|
}
|
|
|
|
|
}
|