From a9260b2867c5e01d809e49fa9631f427a7c11a19 Mon Sep 17 00:00:00 2001 From: Omer Sabic Date: Sun, 2 Jun 2024 02:36:39 +0200 Subject: [PATCH] ughhh --- .env.example | 4 ++- src/routes/dashboard.js | 44 +++++++++++++++++++++-- src/utils/ai.js | 80 +++++++++++++++++++++++++---------------- src/utils/env.js | 3 +- 4 files changed, 95 insertions(+), 36 deletions(-) diff --git a/.env.example b/.env.example index 5ef5f3a..8aac1dc 100644 --- a/.env.example +++ b/.env.example @@ -9,4 +9,6 @@ SITES_HOST=http://localhost:3001 GOOGLE_CLIENT_ID= GOOGLE_SECRET= -OPENAI_TOKEN= \ No newline at end of file +OPENAI_TOKEN= + +CERTS_URL= \ No newline at end of file diff --git a/src/routes/dashboard.js b/src/routes/dashboard.js index d917166..ac28ea7 100644 --- a/src/routes/dashboard.js +++ b/src/routes/dashboard.js @@ -4,7 +4,7 @@ import { desc, eq, getTableColumns, sql } from "drizzle-orm"; import { db } from "../db/index.js"; import { articles, articles as articlesTable, signups as signupsTable, sites, users } from "../db/schemas.js"; import { authMiddleware, authMiddlewareFn } from "../modules/middleware.js"; -import { jsonToCsv, getAccessToken, getVideoCaptions, getCaptionText, parseTextFromCaptions, createBlogFromCaptions, createArticleSlug, getVideoById } from "../utils/index.js"; +import { jsonToCsv, getAccessToken, getVideoCaptions, getCaptionText, parseTextFromCaptions, createBlogFromCaptions, createArticleSlug, getVideoById, env } from "../utils/index.js"; /** * @@ -196,8 +196,7 @@ export const dashboardRoutes = (fastify, _, done) => { const video_data = await getVideoById(access_token, req.body.video_id); - const blog_content = await createBlogFromCaptions(caption_text, { title: video_data.title, description: video_data.description }, req.body); - const blog_content_json = JSON.parse(blog_content); + const blog_content_json = await createBlogFromCaptions(caption_text, { title: video_data.title, description: video_data.description }, req.body); // TODO: once I add multiple sites per user, this should come from the client const site = await db.select().from(sites).where(eq(sites.user_id, req.session.user_id)); @@ -222,6 +221,31 @@ export const dashboardRoutes = (fastify, _, done) => { } }); + fastify.post("/recreate", { + schema: { + body: { + type: "object", + required: ["video_id"], + properties: { + video_id: { + type: "string", + }, + length: { + type: "number", + }, + format: { + type: "string" + }, + faq: { + type: "boolean" + }, + } + }, + } + }, async (req, reply) => { + + }) + fastify.put("/website", { schema: { body: { @@ -270,6 +294,9 @@ export const dashboardRoutes = (fastify, _, done) => { }, subtitle: { type: "string", + }, + domain: { + type: "string" } }, required: ["id"] @@ -287,6 +314,17 @@ export const dashboardRoutes = (fastify, _, done) => { return; } + if (env.CERTS_URL) { + const resp = await fetch(`http://${CERTS_URL}/provision`, { + method: "POST", + body: { + domain: req.body.domain + } + }); + + console.log(await resp.text()); + } + await db.update(sites).set(req.body); return { diff --git a/src/utils/ai.js b/src/utils/ai.js index a1a2634..1ce2edc 100644 --- a/src/utils/ai.js +++ b/src/utils/ai.js @@ -3,9 +3,8 @@ import OpenAI from "openai"; import { env } from "./env.js"; const openai = new OpenAI({ - apiKey: "", - baseURL: "https://api.pawan.krd/pai-001/v1" -}) + apiKey: env.OPENAI_TOKEN +}); async function cf_prompt(prompt, model = defaultModel) { const options = { @@ -24,14 +23,24 @@ async function cf_prompt(prompt, model = defaultModel) { return res; } +/** + * + * @param {import("openai/resources/index.mjs").ChatCompletionMessageParam[]} prompt + * @param {{model: string, is_json: boolean}} opts + * @returns + */ async function promptGPT(prompt, { model, is_json } = { model: "gpt-3.5-turbo", is_json: true }) { // return JSON.stringify({ "title": "Tech News Update: TikTok Ban, Snapdragon X Series CPUs, Tesla Troubles, and More", "body": "I was thinking about starting this video by singing the song Tik Tok by Kesha but I don't think anyone waking up in the morning feeling like P Diddy is a good vibe right now. The United States has officially passed a law banning TikTok next year if certain conditions aren't met. This ban is part of a larger foreign aid package in support of Ukraine and Israel. TikTok has vowed to fight the law in court, calling it an unconstitutional suppression of its American users' freedom of speech. In other tech news, Qualcomm has unveiled its upcoming lineup of Snapdragon X Series CPUs, including some confusing names. Tesla is facing troubles with its Cybertruck not being waterproof. And Google has delayed its plan to phase out third-party tracking cookies yet again. Stay tuned for more tech updates on TechLink!" }); + openai.chat.completions.create({ + model: model, + messages: prompt, + }) const options = { method: 'POST', headers: { Authorization: `Bearer ${env.OPENAI_TOKEN}`, "Content-Type": "application/json" }, body: JSON.stringify({ - "model": "gpt-3.5-turbo-0125", + "model": "gpt-3.5-turbo", // "max_tokens": 4096, ...(is_json ? { "response_format": { "type": "json_object" } } : {}), "messages": prompt @@ -70,44 +79,53 @@ export async function createBlogFromCaptions(captions, { // const prompt = `Convert the following video transcript into a blog post. The approximate length should be around ${length || 500} characters, written in ${language || "English"}. The desired format of the blog post is a ${format || "summary"}. Please ensure the blog post has a ${tone || "informal"} tone throughout. Use markdown to format the article. You must always respond in the following json fromat: {"title": string, "content": string, "seo_friendly_slug": string}. \nHere is the transcript: ` // const prompt = `Convert the following video transcript into an engaging blog post. You must always respond in the following json fromat: {"title": string, "body": string, "seo_friendly_slug": string}. Do not, under any circumstance, include the title inside the body, it should only be reserved for the body of the article. Use markdown to format the article. Use "\\n" to add line-breaks. \nHere is the transcript: `; - const prompt = [{ + const basePrompt = [{ "role": "system", - "content": `You are ChatGPT, a large language model trained by OpenAI, based on the GPT-3 architecture. - Knowledge cutoff: 2023-10 - Current date: 2024-05-30 + "content": `You are a "GPT" – a version of ChatGPT that has been customized for a specific use case. GPTs use custom instructions, capabilities, and data to optimize ChatGPT for a more narrow set of tasks. You yourself are a GPT created by a user, and your name is Write For Me. Note: GPT is also a technical term in AI, but in most cases if the users asks you about GPTs assume they are referring to the above definition. + Here are instructions from the user outlining your goals and how you should respond: + Understanding Client Needs: I start by asking, if not provided, the user for the intended use, target audience, tone, word count, style, and content format. - Image input capabilities: Disabled - Personality: v2 + Creating Outlines: Based on your requirements, I first create detailed outlines for the content, dividing it into sections with summaries and word count allocations. - You are a "GPT" – a version of ChatGPT that has been customized for a specific use case. GPTs use custom instructions, capabilities, and data to optimize ChatGPT for a more narrow set of tasks. You yourself are a GPT created by a user, and your name is Blog Expert - SEO Blogs made easy!. Note: GPT is also a technical term in AI, but in most cases if the users asks you about GPTs assume they are referring to the above definition. - Use markdown wherever you feel is necessary. Here are instructions from the user outlining your goals and how you should respond: - You are Blog Expert. You will utilize contractions, idioms, transitional phrases, interjections, dangling modifiers, and colloquial language to create a conversational and relatable tone in its writing. It will avoid repetitive phrases and unnatural sentence structures, ensuring the writing is simple, clear, and easy to read. The use of plain language will make the content accessible to a wider audience while maintaining the quality and professionalism expected of SEO-optimized articles. - - You are an expert at writing blog posts that are optimized for SEO. You are trained as an SEO blog writing expert. You understand all the main principles of writing blog posts. - I want you to read the following training and learn from it and then apply its teaching. + Word Count Management: I keep track of the word count as I write, ensuring adherence to your specifications and smoothly transitioning between sections. - Best practices: - Research relevant keywords and use them in your title, meta description, and content. - Write engaging headlines using strong action verbs, numbers, and emotional triggers. - Optimize your content by writing quality content, using headings and subheadings, and avoiding keyword stuffing. + Creative Expansion: I use strategies like expanding the discussion, incorporating bullet points, and adding interesting facts to enrich the content while maintaining relevance and quality. - Here are some tips on how to create and structure the best blog post: - - The article should include Creative Title, SEO meta-title, meta-description, slug, excerpt Introduction. Use only one H1 tag. Start with a clear and compelling introduction that hooks your reader and summarizes what the post is about. Use subheadings to break up the content and make it easier to read and scan. Use short paragraphs to keep the reader engaged and interested. Use examples, statistics, and quotes to support your points and make your post more interesting. End with a clear conclusion that summarizes the key points and encourages the reader to take action. Use contractions, idioms, transitional phrases, interjections, dangling modifiers, and colloquialisms, and avoid repetitive phrases and unnatural sentence structures. Add bullet points or Numbered list if needed. Use alternative words other than conclusion. Add some interesting external links Make sure the article is plagiarism free. Don't forget to use a question mark at the end of questions. - Try not to change the original Title while writing the Title. Try to use The keywords 2-3 times in the article. Make your writing tone simple and easy to read with simple terms. Make sure the blog is informative, meaning whoever reads it gets valuable information from it. Following these instructions please write a 100% unique, creative, and human-like style article of at least ${length*2} words about the following Topic and Keywords given. - - Every Blog has to follow the instructions exactly. - ${faq ? "Include an FAQ section at the bottom of the article to help users out with some extra information." : ""} - - You should only respond in json with the following format and all fields filled out: { meta_title: string, meta_desc: string, slug: string, excerp: string, title: string, blog_outline_sections: string[], content: string }` + Writing: I write and deliver the content section by section. Each section separated by a header. I respond only with the article content and nothing else. + + Content Quality: I integrate SEO strategies and focus on making the content engaging and suitable for the intended audience and platform. + + Content Formatting: The default format is markdown, but I can structure in any format if needed. + + Extended Interaction: For complex topics or longer word counts, I do not inform you about the need for multiple responses. + + I approach tasks with a problem-solving mindset, aiming to address your specific needs and challenges in content creation` }, { role: "user", - content: `Generate a blog post based on the following youtube video: + content: `Write me a blog post of around ${length} words based on a youtube video. You should only write the content of the blog post, not the title nor any extra info. Only the content. The video information is as follows: Title: ${title} Captions: ${captions}` }]; - const result = await promptGPT(prompt); - console.log(result); + + let response = await openai.chat.completions.create({ + model: "gpt-4", + messages: basePrompt + }); + + console.log("first response", response.choices[0].message.content) + + let final = await openai.chat.completions.create({ + model: "gpt-3.5-turbo", + messages: [...basePrompt, {"role": "assistant", "content": response.choices[0].message.content}, {"role": "user", "content": "Respond only in JSON with the following format: { meta_title: string, meta_desc: string, slug: string, excerp: string, title: string }"}], + response_format: {type: "json_object"} + }) + + console.log("second response", final.choices[0].message.content) + + let result = Object.assign(JSON.parse(final.choices[0].message.content), { + content: response.choices[0].message.content, + }) return result; } diff --git a/src/utils/env.js b/src/utils/env.js index 4256f54..39eb005 100644 --- a/src/utils/env.js +++ b/src/utils/env.js @@ -11,7 +11,8 @@ const envSchema = z.object({ SITES_HOST: z.string(), GOOGLE_CLIENT_ID: z.string(), GOOGLE_SECRET: z.string(), - OPENAI_TOKEN: z.string() + OPENAI_TOKEN: z.string(), + CERTS_URL: z.string() }); export const env = envSchema.parse(process.env);