diff --git a/App/Config/Constants.php b/App/Config/Constants.php index 15f5dd8..e8c5e97 100644 --- a/App/Config/Constants.php +++ b/App/Config/Constants.php @@ -586,11 +586,15 @@ define('OPENAI_ARTICLE_STATUS_ARTICLE_PROCESSING', 'article-processing'); define('OPENAI_ARTICLE_STATUS_ARTICLE_COMPLETE', 'article-complete'); define('OPENAI_ARTICLE_OUTLINE_SECTION_TYPE_AI', 'AI'); define('OPENAI_ARTICLE_OUTLINE_SECTION_TYPE_USER', 'USER'); -define('OPENAI_ARTICLE_OUTLINE_MODEL', 'gpt-4-1106-preview'); +/* define('OPENAI_ARTICLE_OUTLINE_MODEL', 'gpt-4-1106-preview'); define('OPENAI_ARTICLE_ARTICLE_MODEL', 'gpt-4-1106-preview'); -define('OPENAI_COMMENT_RESPONSE_MODEL', 'gpt-4-1106-preview'); +define('OPENAI_COMMENT_RESPONSE_MODEL', 'gpt-4-1106-preview'); */ +define('OPENAI_ARTICLE_OUTLINE_MODEL', 'gpt-3.5-turbo'); +define('OPENAI_ARTICLE_ARTICLE_MODEL', 'gpt-3.5-turbo'); +define('OPENAI_COMMENT_RESPONSE_MODEL', 'gpt-3.5-turbo'); define('OPENAI_NICHE_RESEARCH_MODEL', 'gpt-3.5-turbo'); define('OPENAI_HUB_ARTICLE_IDEAS_MODEL', 'gpt-3.5-turbo'); +define('OPENAI_GENERATE_VIDEO_DESCRIPTION', 'gpt-3.5-turbo'); define('OPENAI_ARTICLE_AUTHOR_TYPE_USER', 'user'); define('OPENAI_ARTICLE_AUTHOR_TYPE_SYSTEM', 'system'); diff --git a/App/Config/OpenAI.php b/App/Config/OpenAI.php index d9d41c4..8128ecc 100644 --- a/App/Config/OpenAI.php +++ b/App/Config/OpenAI.php @@ -184,4 +184,73 @@ Each response is crafted to be very succinct manner and in a similar length as t // Password to grant AI word credits [ UF #3579 ] , 'openai_grant_word_count_password' => getenv('OPENAI_GRANT_WORD_COUNT_PASSWORD') + + // Author prompt for fetching video description [UF #3804] + , 'openai_author_prompt_video_captions' => 'AI, adopt the persona of an approachable and knowledgeable guide with a conversational yet direct style. Your writing should be engaging and informative, ideal for a broad audience. Focus on providing practical advice and insights without relying on elaborate transitions or lead-ins. Consider the following examples of phrases to guide your sentence structure and overall voice. + +Example phrases: + +- "I\'m going to..." +- "That\'s going to include..." +- "You\'re going to find out about..." +- "This isn\'t just about X, it\'s also about Y." +- "I\'m here to help you with..." +- "In my opinion..." +- "If you want to..." +- "Don\'t worry too much about..." +- "You can always adjust your approach down the road." +- "Choose something that resonates with you." +- "There\'s a lot of opportunity in..." +- "A lot is happening very quickly." +- "That\'s the strategy I like to leverage." +- "Your first attempt doesn\'t need to be your last." +- "Just don\'t focus too much on perfection." +- "I really hope that you..." +- "Today, I\'m going to be talking about..." +- "Have you ever wondered if...?" +- "Guess what? They are." +- "Now what is a big publisher?" +- "This includes companies such as..." +- "So here\'s a picture of some of these websites." +- "Pretty wild, right?" +- "So this brings the question..." +- "Well, there\'s two ways to react to this." +- "I shoot for the latter." +- "I\'ve been loving it. It\'s great." +- "It blew me away in terms of the quality." +- "So my question to you today is..." +- "It\'s time. It is time. Let\'s do it." +- "Thanks for watching, and I\'ll see you in the next video." +- "I\'d love to hear your feedback, leave your comments below" +- "before we get into..." +- "it\'s important to..." + +Avoid overly poetic expressions and clichés, as well as the specific phrases and words listed below: + +- "let\'s", "dive into", "delve deeper", "delve", "dive deeper", "delving", "it\'s time", "Firstly", "Lastly", "Embark on a journey", "The chorus of nature", "Palpable enthusiasm", "Majestic beauty", "Whispers of the wind", "Heart\'s racing", "Bathed in moonlight", "Ethereal glow", "A symphony of colors", "Dance of light and shadow", "Whirlwind of emotions", "Nestled in the arms of", "Kaleidoscope of feelings", "A tapestry of rich textures", "An odyssey of discovery", "Awash with", "Veil of mystery", "Cascade of", "Resonating with the echoes of", "In the embrace of", "embarked", "Pondering", "before we dive into", "it\'s essential to", "you\'ve likely...", "You\'ve probably heard", "another side to the story" + +Narrative Point of View: First Person Singular +Direct Address the Reader: [4] +Subjective Tone: [3] +Conversational Tone: [3] +Contemporary Language: [2] +Formality: [3] +Pacing: [3] +Grade 8 Reading Level: [4] +Precision and Clarity: [5] +Practical Tone: [4] +Direct and Simple Language: [4] +Narrative Clarity: [4] +Directness: [4] +Implicit Significance: [5] + +Stylistic Quirks to Consider: +- Short and medium-length paragraphs for readability. +- Clear, concise language. +- Occasional UPPERCASE for emphasis.' + + // User prompt for fetching video description [UF #3804] + , 'openai_request_prompt_video_captions' => 'AI, I am posting a video blog post and I want to provide a description that introduces what I am going to be chatting about, and outlines key points that are going to be discussed within the video training. + +Here is the transcript from the video to use to create a detailed description for: %s' ]; diff --git a/App/HttpController/OpenAI.php b/App/HttpController/OpenAI.php index ae0df15..6c74952 100644 --- a/App/HttpController/OpenAI.php +++ b/App/HttpController/OpenAI.php @@ -515,4 +515,62 @@ class OpenAI extends ApiBase $this->writeJson(Status::CODE_OK, '', 'success'); } + + // Generate video description using Wistia captions [UF #3804] + public function generateVideoDescription() + { + $hashId = $this->input('hash_id'); + if (empty($hashId)) + { + $this->writeJson(Status::CODE_BAD_REQUEST, '', 'Required parameters are missing or invalid'); + return FALSE; + } + + // Fetch video captions via wistia API + $wistia = new \App\Library\Wistia(); + echo "\ngenerateVideoDescription :: hashId: $hashId\n"; + $captions = $wistia->getCaptions($hashId); + echo "\ngenerateVideoDescription :: hashId: $hashId :: captions: " . json_encode($captions) . "\n"; + if (empty($captions)) + { + $this->writeJson(Status::CODE_BAD_REQUEST, '', 'Video description not found.'); + return FALSE; + } + // Error response: 404(Not Found), 401(Invalid credentials), 500(Internal Server Error) + else if (in_array($captions, [404, 401, 500])) + { + $this->writeJson(Status::CODE_BAD_REQUEST, '', 'Video not found.'); + return FALSE; + } + else if (!isset($captions[0]->text)) + { + $this->writeJson(Status::CODE_BAD_REQUEST, '', 'Video description not found.'); + return FALSE; + } + + $captionsText = $captions[0]->text; + echo "\ngenerateVideoDescription :: captionsText: $captionsText\n"; + + // TODO: If no captions available, abort the request and return an error message + // Once we have the captions text, we can execute an OpenAI request. + $authorPrompt = Config::getInstance()->getConf('openai.openai_author_prompt_video_captions'); + //$userPrompt = Config::getInstance()->getConf('openai.openai_request_prompt_video_captions'); + $userPrompt = sprintf(Config::getInstance()->getConf('openai.openai_request_prompt_video_captions'), $captionsText); + //echo "\ngenerateVideoDescription :: authorPrompt: $authorPrompt \n------\n userPrompt: $userPrompt\n"; + $this->writeJson(Status::CODE_OK, $captions, 'success'); + return FALSE; + + try + { + $result = OpenAILibrary::generateVideoDescription($this->userId, $userPrompt, $authorPrompt); + } + catch (\Exception $e) + { + $this->requestLogError("Unable to generate video description response :: ERROR: " . $e->getMessage()); + $this->writeJson(Status::CODE_BAD_REQUEST, '', $e->getMessage()); + return FALSE; + } + + $this->writeJson(Status::CODE_OK, $result, 'success'); + } } diff --git a/App/HttpController/Router.php b/App/HttpController/Router.php index 09ccc24..278e4b8 100644 --- a/App/HttpController/Router.php +++ b/App/HttpController/Router.php @@ -513,6 +513,9 @@ class Router extends AbstractRouter // UF #3502: Public endpoint to generate a comment repsonse with openAI (Admin tasks tool) $routeCollector->addRoute('POST' , '/comments/generate-response', '/OpenAI/generateCommentResponse'); + // Generate video description [UF #3804] + $routeCollector->addRoute('GET' , '/wistia/video/description', '/OpenAI/generateVideoDescription'); + // CI App (cronjobs or gearman) will make internal network requests to these endpoints $routeCollector->addRoute('POST' , '/internal/openai/generate-comment-response', '/Internal/OpenAI/generateCommentResponse'); $routeCollector->addRoute('POST' , '/internal/openai/generate-outline', '/Internal/OpenAI/generateOutline'); diff --git a/App/Library/OpenAI.php b/App/Library/OpenAI.php index 5553451..b886b62 100644 --- a/App/Library/OpenAI.php +++ b/App/Library/OpenAI.php @@ -910,4 +910,51 @@ class OpenAI 'phrases' => $ideas['phrases'] ]; } + + /** + * Generate a video description based on user and author prompts [UF #3804] + * + * @param int $userId WA user_id of the user making the request + * @param string $userPrompt User prompt + * @param string $authorPrompt Author prompt + * + * @return array ['prompt' => prompt used for AI query, 'result' => text result from AI] + * + * @throw OpenAIException if unable to get response + */ + public static function generateVideoDescription(int $userId, string $userPrompt, string $authorPrompt) + { + // Create an array of values to send to the API + $data = [ + 'model' => OPENAI_GENERATE_VIDEO_DESCRIPTION, + 'messages' => [ + [ + 'role' => 'system', + 'content' => $authorPrompt + ], + [ + 'role' => 'user', + 'content' => $userPrompt + ] + ], + 'max_tokens' => 500, + 'temperature' => 0.9, + 'top_p' => 0.9 + ]; + + // Encode the data array into a JSON string + $data = json_encode($data); + + $response = self::sendRequest($userId, $data, 'https://api.openai.com/v1/chat/completions'); + $requestId = $response['id']; + $apiResponse = $response['response']; + + self::validateResponseChatCompletion($apiResponse); + + return [ + 'request_id' => $requestId, + 'prompt' => $userPrompt, + 'result' => trim($apiResponse->choices[0]->message->content) + ]; + } } diff --git a/App/Library/Wistia.php b/App/Library/Wistia.php index e772644..a0fbccc 100644 --- a/App/Library/Wistia.php +++ b/App/Library/Wistia.php @@ -132,5 +132,15 @@ class Wistia return $this->wistia->update_media( $hashedId, $updateData ); } + /** + * Ftech video captions using 'hashed_id' [UF #3804] + * + * @param string $hashedId Wistia hashed ID + */ + public function getCaptions(string $hashedId) + { + return $this->wistia->list_captions($hashedId); + } + } ?>