From 4803ed39db78b496aa94afc50336a4e77dee383d Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Fri, 31 May 2024 17:27:50 -0700 Subject: [PATCH] add no framework agent --- 2024-05-31-agentframeworks/bun.lockb | Bin 0 -> 5958 bytes 2024-05-31-agentframeworks/package.json | 16 +++ 2024-05-31-agentframeworks/prompts.ts | 5 + 2024-05-31-agentframeworks/tsconfig.json | 27 +++++ 2024-05-31-agentframeworks/videoSearchTool.ts | 108 ++++++++++++++++++ 2024-05-31-agentframeworks/ytresearch.ts | 58 ++++++++++ 6 files changed, 214 insertions(+) create mode 100755 2024-05-31-agentframeworks/bun.lockb create mode 100644 2024-05-31-agentframeworks/package.json create mode 100644 2024-05-31-agentframeworks/prompts.ts create mode 100644 2024-05-31-agentframeworks/tsconfig.json create mode 100644 2024-05-31-agentframeworks/videoSearchTool.ts create mode 100644 2024-05-31-agentframeworks/ytresearch.ts diff --git a/2024-05-31-agentframeworks/bun.lockb b/2024-05-31-agentframeworks/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..93403e5be9e63630b67f3d727b6b06f4154d2603 GIT binary patch literal 5958 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 h@SsN{xd>E!4?yG&NdivMN5voT=b!IYjZOdn literal 0 HcmV?d00001 diff --git a/2024-05-31-agentframeworks/package.json b/2024-05-31-agentframeworks/package.json new file mode 100644 index 0000000..a2bef64 --- /dev/null +++ b/2024-05-31-agentframeworks/package.json @@ -0,0 +1,16 @@ +{ + "name": "noframeworkagents", + "module": "index.ts", + "type": "module", + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "dirty-json": "^0.9.2", + "ollama": "^0.5.1", + "readline": "^1.3.0" + } +} \ No newline at end of file diff --git a/2024-05-31-agentframeworks/prompts.ts b/2024-05-31-agentframeworks/prompts.ts new file mode 100644 index 0000000..3166cd4 --- /dev/null +++ b/2024-05-31-agentframeworks/prompts.ts @@ -0,0 +1,5 @@ +export const titleprompt = "You will be given a topic, a description, and a list of high performing titles for a similar topic. Create 10 potential titles for this video topic and description. It is very important to use the list of videos to help you generate the titles. The titles should be less than 70 characters and should have a high click-through-rate." + +export const descriptionprompt = "Create a great description for a video based on the information provided by the user. This description will be add to the video on YouTube when it is submitted. The description should be 200 to 500 words. At the end, include something convincing to encourage the user to subscribe to the newsletter at technovangelist.com\/newsletter, and to join the Patreon at patreon.com\/technovangelist." + +export const announcementprompt = "Every time I have a new video, I like to send out an email to my subscribers. Please create an email to send to that list based on the topic and description provided by the user. The email should be 300 to 600 words and include a place for the subscriber\'s name, what the viewer will learn from the video, and a link to the video that will render in an email. also encourage the reader to sign up for my patreon if they want to support me further. The email should be signed off from me, Matt Williams." \ No newline at end of file diff --git a/2024-05-31-agentframeworks/tsconfig.json b/2024-05-31-agentframeworks/tsconfig.json new file mode 100644 index 0000000..238655f --- /dev/null +++ b/2024-05-31-agentframeworks/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/2024-05-31-agentframeworks/videoSearchTool.ts b/2024-05-31-agentframeworks/videoSearchTool.ts new file mode 100644 index 0000000..365336e --- /dev/null +++ b/2024-05-31-agentframeworks/videoSearchTool.ts @@ -0,0 +1,108 @@ +import * as readline from "readline/promises"; + +type VideoSearchResults = { + id: string; + title: string; + channelId: string; + channelTitle: string; + daysSincePublished: number; +} + +type VideoDetails = { + videoString: string; + title: string; + viewCount: number; + likeCount: number; + commentCount: number; + subscriberCount: number; + score: number; +} + +const getInput = async (question: string): Promise => { + let output = ""; + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + output = await rl.question(question); + rl.close(); + return output; +} + +export const getTopic = async (): Promise => { + const topic = await getInput("Enter a topic to search for: "); + return topic; +} + +export const getDetails = async (topic: string): Promise => { + const details = await getInput(`Enter a description for the video on ${topic}: `); + return details; +} + +export const videoSearchTool = async (topic: string, maxResults: number = 35): Promise => { + + const results: VideoSearchResults[] = []; + topic = encodeURIComponent(topic); + const apiKey = Bun.env.YouTubeAPIKey; + const url = `https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=${maxResults}&q=${topic}&type=video&key=${apiKey}`; + + const response = await fetch(url); + const data = await response.json(); + + if (data.items) { + for (const item of data.items) { + const id = item.id.videoId; + const title = item.snippet.title; + const channelId = item.snippet.channelId; + const channelTitle = item.snippet.channelTitle; + const daysSincePublished = Math.floor((new Date().getTime() - new Date(item.snippet.publishedAt.slice(0, 10)).getTime())/(1000*60*60*24)); + + results.push({ + id, + title, + channelId, + channelTitle, + daysSincePublished, + }); + } + } + + return results; +} + +export const videoDetailsTool = async (video: VideoSearchResults): Promise => { + +} + + const apiKey = Bun.env.YouTubeAPIKey; + const videoUrl = `https://www.googleapis.com/youtube/v3/videos?part=snippet,statistics&id=${video.id}&key=${apiKey}`; + const channelUrl = `https://www.googleapis.com/youtube/v3/channels?part=statistics&id=${video.channelId}&key=${apiKey}`; + + const videoResponse = await fetch(videoUrl); + const channelResponse = await fetch(channelUrl); + + const videoData = await videoResponse.json(); + const channelData = await channelResponse.json(); + + const title = videoData.items[0].snippet.title; + const viewCount = (videoData.items[0].statistics.viewCount as number); + const likeCount = videoData.items[0].statistics.likeCount; + const commentCount = videoData.items[0].statistics.commentCount; + const subscriberCount = channelData.items[0].statistics.subscriberCount; + + const videoString = ` - Title: ${title}\n - Channel: ${video.channelTitle}\n - View Count: ${Number(viewCount).toLocaleString()}\n - Days Since Published: ${video.daysSincePublished}\n - Likes: ${Number(likeCount).toLocaleString()}\n - Subscriber Count: ${Number(subscriberCount).toLocaleString('en-US')}\n - Video URL: https://www.youtube.com/watch?v=${video.id}\n`; + + // const score = (viewCount / video.daysSincePublished / subscriberCount) + (likeCount * 10) + (commentCount * 100); + const score = (((viewCount / subscriberCount) * 0.4) + ((likeCount / viewCount) * 0.3) + ((commentCount / viewCount) * 0.2) * (1/video.daysSincePublished)) ; + + return { + videoString, + title, + viewCount, + likeCount, + commentCount, + subscriberCount, + score + }; + +} diff --git a/2024-05-31-agentframeworks/ytresearch.ts b/2024-05-31-agentframeworks/ytresearch.ts new file mode 100644 index 0000000..8a694b0 --- /dev/null +++ b/2024-05-31-agentframeworks/ytresearch.ts @@ -0,0 +1,58 @@ +import ollama from "ollama"; +import * as djson from "dirty-json"; +import { getTopic, getDetails, videoSearchTool, videoDetailsTool } from "./videoSearchTool.ts"; +import { titleprompt, descriptionprompt, announcementprompt } from "./prompts.ts"; + +// const topic = await getTopic(); +// const details = await getDetails(topic); +const topic = "ai models local ollama" +const details = "You are going to create a new model for ollama. use the modelfile to create an AI model in creative ways and do interesting things using parameters, system prompt, and more."; + +const videos = await videoSearchTool(topic, 50) +let scoredVideos = [] +for (const video of videos) { + const videodetails = await videoDetailsTool(video); + scoredVideos.push(videodetails); +} + +scoredVideos = scoredVideos.sort((a, b) => b.score - a.score).slice(0, 15); +scoredVideos.forEach((video, index) => { + console.log(`Video ${index + 1}: ${video.score}`); + console.log(video.videoString) +}); + +const rawtitlelist = await ollama.generate({ + model: "llama3", + system: titleprompt, + format: "json", + prompt: `topic: ${topic}\nDescription: ${details}\nSuccessful titles:\n${scoredVideos.map(video => video.title).join("\n")}. Output as JSON, using this template: {titles: ['title1', 'title2']}`, +}) +const titlelist = JSON.parse(rawtitlelist.response); + +console.log(`Successful Titles for the topic: ${topic}\n\n${titlelist.titles.map((s: string) => `- ${s}`).join("\n")}\n\n`); + +const rawnewdescription = await ollama.generate({ + model: "llama3", + system: descriptionprompt, + format: "json", + prompt: ` topic: ${topic}\nDescription: ${details}\n Output as JSON, using this template: \n\n{'output': 'description of the video'} ` +}); +const newdescription = djson.parse(rawnewdescription.response).output; +console.log(`Description for the video based on: ${details}\n${newdescription}\n\n`); + +const rawemail = await ollama.generate({ + model: "llama3", + system: announcementprompt, + format: "json", + prompt: `topic: ${topic}\nDescription: ${newdescription}\nOutput as JSON, using this template: \n{'output': 'text of the email'}`, +}); +const email = djson.parse(rawemail.response).output; + +console.log(`Email for the video based on the generated description: \n${email}\n\n`); + + + + + + +