Files
memefast/app/Helpers/FirstParty/AI/OpenAI.php
2025-06-28 21:33:30 +08:00

256 lines
14 KiB
PHP

<?php
namespace App\Helpers\FirstParty\AI;
use Illuminate\Support\Facades\Http;
class OpenAI
{
public static function getMemeKeywords(string $name, string $description)
{
$apiKey = config('services.openai.api_key'); // Make sure to set this in config/services.php
$response = Http::withHeaders([
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $apiKey,
])->post('https://api.openai.com/v1/responses', [
'model' => 'gpt-4.1-nano',
'input' => [
[
'role' => 'system',
'content' => [
[
'type' => 'input_text',
'text' => "You are a meme annotation assistant. Given a meme name and description, you must populate three keyword categories: action_keywords: List specific actions being performed in the meme (e.g., wearing, singing, dancing) emotion_keywords: List feelings and emotions conveyed by the meme (e.g., joy, excitement, sadness) misc_keywords: List core identifying elements - keep this concise and focused on essential references only (e.g., main subjects, key objects, proper nouns) Format your response with clear category labels. Be precise and avoid over-elaborating, especially in misc_keywords. Example: Input: \"7th Element OIIA Cat, OIIA cat wearing 7th element head ban and sings 7th element song chorous with OIIA sounds\" Output: action_keywords: wearing, singing, performing emotion_keywords: excitement, joy, playfulness misc_keywords: cat, 7th element, headband, OIIA. the description may also have spelling and grammar issues, please fix it\n\nreturn in json:\n{\ndescription: \"\",\naction_keywords:[],\nemotion_keywords:[],\nmisc_keywords:[],\n}",
],
],
],
[
'role' => 'user',
'content' => [
[
'type' => 'input_text',
'text' => "Name: $name\nDescription: $description",
],
],
],
],
'text' => [
'format' => [
'type' => 'json_object',
],
],
'reasoning' => new \stdClass,
'tools' => [],
'temperature' => 1,
'max_output_tokens' => 2048,
'top_p' => 1,
'store' => true,
]);
return $response->json();
}
public static function getSingleMemeGenerator($user_prompt)
{
$captions = [
[
'caption_description' => 'A humorous, funny one-liner meme caption that starts with "When you", describes a specific visual or situational moment, avoids vagueness, and ends with no punctuation',
'caption_style' => 'Relatable one-liners starting with "When you"'
],
[
'caption_description' => 'A POV meme that starts with "POV: ", clearly describes a specific scenario or feeling with high context, and ends with no punctuation',
'caption_style' => 'POV-style captions (e.g., "POV: finally logging off after pretending to work for 3 hours")'
],
[
'caption_description' => 'A humorous, funny one-liner meme caption that starts with "I", grounded in a relatable, situational experience with visual context, and ends with no punctuation',
'caption_style' => 'Relatable one-liners starting with "I"'
],
[
'caption_description' => 'A humorous, funny one-liner meme caption that starts with "You", focused on a clear, specific reaction or moment, and ends with no punctuation',
'caption_style' => 'Relatable one-liners starting with "You"'
],
[
'caption_description' => 'A humorous, funny one-liner meme caption with no punctuation that describes a vivid, realistic scenario or reaction in a clearly defined context',
'caption_style' => 'Visual punchlines (e.g., "Wearing a suit and contemplating my life in the elevator")'
],
[
'caption_description' => 'A juxtaposition-style one-liner meme caption expressing a contrast or contradiction in a witty, punchy way, often revealing irony or absurdity, and ends with no punctuation (e.g., "I want a salary without a job"). Start with "I" or "You" to create a sense of contrast',
'caption_style' => 'Juxtaposition (e.g., "I want a salary without a job")'
],
[
'caption_description' => 'A meme caption that starts with "TIL: ", presents a short, punchy, and ironic realization about work or life that feels meme-worthy, clearly sets up a visual or exaggerated truth, and ends with no punctuation (e.g., "TIL: rent is just a subscription to be alive")',
'caption_style' => 'TIL captions (e.g., "TIL: rent is just a subscription to be alive")'
],
[
'caption_description' => 'A meme caption that starts with "TL;DR: ", provides a blunt, dry, or brutally honest summary of a situation, ideally workplace- or life-related, and ends with no punctuation (e.g., "TL;DR: we had a meeting about having fewer meetings")',
'caption_style' => 'TL;DR captions (e.g., "TL;DR: we had a meeting about having fewer meetings")'
],
[
'caption_description' => 'A meme caption that starts with "The moment you realize", sets up an unexpected or awkward realization in a relatable work or life setting, and ends with no punctuation',
'caption_style' => 'The moment you realize... (e.g., "The moment you realize your boss joined the call 5 minutes ago")'
],
[
'caption_description' => 'A meme caption that uses the "Nobody:" format to exaggerate a reaction or absurd behavior, often with a silent or empty setup followed by an over-the-top response, and ends with no punctuation',
'caption_style' => 'Nobody: ... (e.g., "Nobody: \nMe: sends 3 follow-up emails and panics")'
],
[
'caption_description' => 'A meme caption that starts with "Me trying to", describes a personal struggle or awkward attempt to do something relatable, and ends with no punctuation',
'caption_style' => 'Me trying to... (e.g., "Me trying to stay calm after 2 hours on hold")'
],
[
'caption_description' => 'A meme caption that starts with "It\'s giving", followed by a cultural, emotional, or exaggerated vibe that humorously labels the situation, and ends with no punctuation',
'caption_style' => 'It\'s giving... (e.g., "It\'s giving corporate despair in HD")'
],
[
'caption_description' => 'A meme caption that starts with "Meanwhile", contrasts expected behavior with a chaotic or unexpected reality, and ends with no punctuation',
'caption_style' => 'Meanwhile... (e.g., "Meanwhile: I\'m Googling how to quit politely")'
],
[
'caption_description' => 'A meme caption that starts with "My toxic trait is", followed by an ironic or self-aware confession that highlights flawed logic or behavior, and ends with no punctuation',
'caption_style' => 'My toxic trait is... (e.g., "My toxic trait is checking Slack and getting mad")'
],
];
//$random_caption = $captions[rand(0, count($captions) - 1)];
$random_caption = $captions[count($captions) - 1];
$response = Http::withHeaders([
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . env('OPENAI_API_KEY'),
])
->post('https://api.openai.com/v1/responses', [
'model' => 'gpt-4.1-nano',
'input' => [
[
'role' => 'system',
'content' => [
[
'type' => 'input_text',
'text' => "You are an AI meme writer for a video meme generator.
Your task is to generate a JSON array containing a unique, short-form workplace-related video meme idea.
Each meme object must include the following fields, with the format shown below. Each field contains an inline description of the type of content it must generate.
Only use this style for the caption:
\"{$random_caption['caption_style']}\"
Return **only** a Markdown code block containing valid JSON with inline content guidance (as strings). Do not add any commentary or explanation outside the code block.
All memes must be funny, relatable, and clearly connected to the provided topic.
Use realistic but exaggerated expressions and settings to match meme-style content.
Style should suit video formats like TikTok, Instagram Reels, or YouTube Shorts.",
],
],
],
[
'role' => 'user',
'content' => [
[
'type' => 'input_text',
'text' => $user_prompt,
],
],
],
],
'text' => [
'format' => [
'type' => 'json_schema',
'name' => 'meme_generator_single_1',
'strict' => true,
'schema' => [
'type' => 'object',
'properties' => [
'caption' => [
'type' => 'string',
'description' => $random_caption['caption_description'],
],
'primary_keyword_type' => [
'type' => 'string',
'description' => 'Primary keyword type, choose only between: (action|emotion|misc)',
],
'action_keywords' => [
'type' => 'array',
'description' => 'List specific actions being performed in the meme (e.g., wearing, singing, dancing)',
'items' => [
'type' => 'string',
],
],
'emotion_keywords' => [
'type' => 'array',
'description' => 'List feelings and emotions conveyed by the meme (e.g., joy, excitement, sadness)',
'items' => [
'type' => 'string',
],
],
'misc_keywords' => [
'type' => 'array',
'description' => 'List core identifying elements - keep this concise and focused on essential references only (e.g., main subjects, key objects, proper nouns)',
'items' => [
'type' => 'string',
],
],
'background' => [
'type' => 'string',
'description' => "Use this exact structure: 'Location: [setting only, e.g., empty office with cluttered desk and monitor]'. Do not mention people, animals, actions, or any living beings. No verbs. No brand names. Only interior spaces or physical locations. Examples: 'Location: quiet office cubicle with headset and papers'.",
],
'keywords' => [
'type' => 'array',
'description' => 'Relevant lowercase tags for the meme (e.g., "burnout", "email fail", "meetings")',
'items' => [
'type' => 'string',
],
],
],
'required' => [
'caption',
'primary_keyword_type',
'action_keywords',
'emotion_keywords',
'misc_keywords',
'background',
'keywords',
],
'additionalProperties' => false,
],
],
],
'reasoning' => (object) [],
'tools' => [],
'temperature' => 1,
'max_output_tokens' => 2048,
'top_p' => 1,
'store' => true,
]);
$data = $response->json();
if ($response->successful()) {
return $data;
} else {
throw new \Exception('API request failed: ' . $response->body());
}
}
public static function getOpenAIOutput($data)
{
// dump($data);
$output = null;
$response_type = data_get($data, 'output.0.content.0.type', null);
if ($response_type === 'output_text') {
$output = data_get($data, 'output.0.content.0.text', null);
} elseif ($response_type === 'refusal') {
$output = data_get($data, 'output.0.content.0.refusal', null);
}
return $output;
}
}