import { OpenAI } from "https://deno.land/x/openai@1.4.2/mod.ts"; import { OPENAI_API_KEY } from "@lib/env.ts"; import { cacheFunction } from "@lib/cache/cache.ts"; import { hashString } from "@lib/helpers.ts"; const openAI = OPENAI_API_KEY && new OpenAI(OPENAI_API_KEY); function extractListFromResponse(response?: string): string[] { if (!response) return []; return response .split(/[\n,]/) .map((line) => line.trim()) .filter((line) => !line.endsWith(":")) .map((line) => line.replace(/^[^\s]*/, "").trim()) .filter((line) => line.length > 2); } export async function summarize(content: string) { if (!openAI) return; const chatCompletion = await openAI.createChatCompletion({ model: "gpt-3.5-turbo", messages: [ { "role": "system", "content": "You are a helpful assistant." }, { "role": "user", "content": content.slice(0, 2000) }, { "role": "user", "content": "Please summarize the article in one sentence as short as possible", }, ], }); return chatCompletion.choices[0].message.content; } export async function shortenTitle(content: string) { if (!openAI) return; const chatCompletion = await openAI.createChatCompletion({ model: "gpt-3.5-turbo", messages: [ { "role": "system", "content": "You are a helpful assistant." }, { "role": "user", "content": content.slice(0, 2000) }, { "role": "user", "content": "Please shorten the provided website title as much as possible, don't rewrite it, just remove unneccesary informations. Please remove for example any mention of the name of the website.", }, ], }); return chatCompletion.choices[0].message.content; } export async function extractAuthorName(content: string) { if (!openAI) return; const chatCompletion = await openAI.createChatCompletion({ model: "gpt-3.5-turbo", messages: [ { "role": "system", "content": "You are a helpful assistant." }, { "role": "user", "content": content.slice(0, 2000) }, { "role": "user", "content": "If you are able to extract the name of the author from the text please respond with the name, otherwise respond with 'not found'", }, ], }); const author = chatCompletion.choices[0].message.content; if ( author?.toLowerCase().includes("not") && author?.toLowerCase().includes("found") ) return ""; return author; } export async function createGenres( type: string, description: string, title = "unknown", ) { if (!openAI) return; const chatCompletion = await openAI.createChatCompletion({ model: "gpt-3.5-turbo", messages: [ { "role": "system", "content": `you create some keywords that can be used in a recommendation system. The keywords are based on a ${type} description or title. If you do not know the title, take into account the description aswell. Create a range of keywords from very specific ones that describe the general vibe. ${ title ? `The name of the ${type} is ${title}` : "" }`, }, { "role": "user", "content": `description: ${description.slice(0, 2000)}`, }, { "role": "user", "content": "return a list of around 20 keywords seperated by commas", }, ], }); const res = chatCompletion.choices[0].message.content?.toLowerCase(); return extractListFromResponse(res) .map((v) => v.replaceAll(" ", "-")); } export async function createKeywords( type: string, description: string, title = "unknown", ) { if (!openAI) return; const chatCompletion = await openAI.createChatCompletion({ model: "gpt-3.5-turbo", messages: [ { "role": "system", "content": `you create some keywords that can be used in a recommendation system. The keywords are based on a ${type} description or title. If you do not know the title, take into account the description aswell. Create a range of keywords from very specific ones that describe the general vibe. title: ${title} description: ${description.slice(0, 2000).replaceAll("\n", " ")}} `, }, { "role": "user", "content": "return a list of around 20 keywords seperated by commas", }, ], }); const res = chatCompletion.choices[0].message.content?.toLowerCase(); return extractListFromResponse(res) .map((v) => v.replaceAll(" ", "-")); } export const getMovieRecommendations = (keywords: string, exclude: string[]) => cacheFunction({ fn: async () => { if (!openAI) return; const chatCompletion = await openAI.createChatCompletion({ model: "gpt-3.5-turbo", messages: [ { "role": "system", "content": `Could you recommend me 10 movies based on the following attributes: ${keywords} The movies should be similar to but not include ${ exclude.join(", ") } or remakes of that. respond with a plain unordered list each item starting with the year the movie was released and then the title of the movie seperated by a -`, }, ], }); const res = chatCompletion.choices[0].message.content?.toLowerCase(); if (!res) return; console.log("REsult:"); console.log(res); const list = extractListFromResponse(res); console.log({ list }); return res.split("\n").map((entry) => { const [year, ...title] = entry.split("-"); return { year: parseInt(year.trim()), title: title.join(" ").replaceAll('"', "").trim(), }; }).filter((y) => !Number.isNaN(y.year)); }, id: `openai:movierecs:${hashString(`${keywords}:${exclude.join()}`)}`, }); export async function createTags(content: string) { if (!openAI) return; const chatCompletion = await openAI.createChatCompletion({ model: "gpt-3.5-turbo", messages: [ { "role": "system", "content": "You are a helpful assistant." }, { "role": "user", "content": content.slice(0, 2000) }, { "role": "user", "content": "Please respond with a list of genres the corresponding article falls into, dont include any other informations, just a comma seperated list of top 5 categories. Please respond only with tags that make sense even if there are less than five.", }, ], }); const res = chatCompletion.choices[0].message.content?.toLowerCase(); return extractListFromResponse(res).map((v) => v.replaceAll(" ", "-")); }