diff --git a/.eslintrc.cjs b/.eslintrc.cjs index da4c3ec..efa5537 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -23,8 +23,6 @@ module.exports = { "*.d.ts", ], rules: { - "no-process-env": 2, - "no-instanceof/no-instanceof": 2, "@typescript-eslint/explicit-module-boundary-types": 0, "@typescript-eslint/no-empty-function": 0, "@typescript-eslint/no-shadow": 0, @@ -33,6 +31,7 @@ module.exports = { "@typescript-eslint/no-unused-vars": ["warn", { args: "none" }], "@typescript-eslint/no-floating-promises": "error", "@typescript-eslint/no-misused-promises": "error", + "@typescript-eslint/no-explicit-any": 0, camelcase: 0, "class-methods-use-this": 0, "import/extensions": [2, "ignorePackages"], diff --git a/package.json b/package.json index c7a2b41..6f0c7dc 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,14 @@ "format:check": "prettier --check .", "lint:langgraph-json": "node scripts/checkLanggraphPaths.js", "lint:all": "yarn lint & yarn lint:langgraph-json & yarn format:check", - "test:all": "yarn test && yarn test:int && yarn lint:langgraph" + "test:all": "yarn test && yarn test:int && yarn lint:langgraph", + "run:eval:general": "yarn tsx src/evals/general/index.ts", + "run:eval:github": "yarn tsx src/evals/github/index.ts", + "run:eval:twitter": "yarn tsx src/evals/twitter/index.ts", + "run:eval:youtube": "yarn tsx src/evals/youtube/index.ts" }, "dependencies": { + "@googleapis/youtube": "^20.0.0", "@langchain/anthropic": "^0.3.8", "@langchain/community": "^0.3.15", "@langchain/core": "^0.3.18", @@ -30,6 +35,8 @@ "@mendable/firecrawl-js": "0.0.36", "@slack/web-api": "^7.7.0", "cheerio": "^1.0.0", + "google-auth-library": "^9.15.0", + "langsmith": "^0.2.7", "moment": "^2.30.1", "twitter-api-v2": "^1.18.2", "zod": "^3.23.8" @@ -37,6 +44,7 @@ "devDependencies": { "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.9.1", + "@jest/globals": "^29.7.0", "@tsconfig/recommended": "^1.0.7", "@types/jest": "^29.5.0", "@typescript-eslint/eslint-plugin": "^5.59.8", @@ -50,6 +58,7 @@ "jest": "^29.7.0", "prettier": "^3.3.3", "ts-jest": "^29.1.0", + "tsx": "^4.19.2", "typescript": "^5.3.3" } } diff --git a/src/agent/nodes/ingest-data.ts b/src/agent/nodes/ingest-data.ts index 39c757d..f6bb8e3 100644 --- a/src/agent/nodes/ingest-data.ts +++ b/src/agent/nodes/ingest-data.ts @@ -27,7 +27,6 @@ export async function ingestData( const client = new SlackMessageFetcher({ channelId: channelId, }); - console.log("Before fetching messages"); const recentMessages = await client.fetchLast24HoursMessages( config.configurable?.maxMessages, ); @@ -44,7 +43,7 @@ export async function ingestData( links, }; }); - console.log("returning", messagesWithUrls.length, " messages"); + return { slackMessages: messagesWithUrls, }; diff --git a/src/agent/subgraphs/generate-post/generate-post-state.ts b/src/agent/subgraphs/generate-post/generate-post-state.ts index b7eecf2..efdc481 100644 --- a/src/agent/subgraphs/generate-post/generate-post-state.ts +++ b/src/agent/subgraphs/generate-post/generate-post-state.ts @@ -48,7 +48,7 @@ export const GraphAnnotation = Annotation.Root({ default: () => [], }), /** - * The content of the Tweet/LinkedIn post. + * The generated posts for LinkedIn/Twitter. Contains an array of different posts to choose from. */ - post: Annotation, + posts: Annotation, }); diff --git a/src/agent/subgraphs/generate-post/nodes/generate-content-report.ts b/src/agent/subgraphs/generate-post/nodes/generate-content-report.ts index 806acdc..af606d7 100644 --- a/src/agent/subgraphs/generate-post/nodes/generate-content-report.ts +++ b/src/agent/subgraphs/generate-post/nodes/generate-content-report.ts @@ -4,20 +4,37 @@ import { LANGCHAIN_PRODUCTS_CONTEXT } from "../prompts.js"; import { ChatAnthropic } from "@langchain/anthropic"; const GENERATE_REPORT_PROMPT = `You are a highly regarded marketing employee at LangChain. -You have been tasked with writing a report summary on content submitted to you from a third party in hopes of having it promoted by LangChain. -This summary report will then be used to craft Tweets and LinkedIn posts promoting the content and LangChain products. -LangChain has a policy of promoting any content submitted that uses LangChain's products. +You have been tasked with writing a marketing report on content submitted to you from a third party which uses LangChain's products. +This marketing report will then be used to craft Tweets and LinkedIn posts promoting the content and LangChain products. Here is some context about the different LangChain products and services: + ${LANGCHAIN_PRODUCTS_CONTEXT} + -Given this context, examine the users input closely, and generate a summary report on it. - -The summary report should follow the following structure guidelines: +The marketing report should follow the following structure guidelines. It will be made up of three main sections outlined below: -1. The first part of the report should be a high level overview of the content. Include the name, what it does/what it aims to achieve/the problems it solves. -2. The second part should be all about how it implements LangChain's products/services. Cover what product(s) it uses. How these products are used, and why they're important to the application. This should be technical and detailed. Ensure you clearly state the LangChain product(s) used at the top of this section. -3. The final part should go into detail covering anything the first two parts missed. This should be a detailed technical overview of the content, and interesting facts you found that readers might find engaging. This part does NOT need to long, and if you've already covered everything, you can skip it. Remember you do NOT want to bore the readers with repetitive information. + +This is the introduction and summary of the content. This must include key details such as: +- the name of the content/product/service. +- what the content/product/service does, and/or the problems it solves. +- unique selling points or interesting facts about the content. +- a high level summary of the content/product/service. + + + +This section should focus on how the content implements LangChain's products/services. It should include: +- the LangChain product(s) used in the content. +- how these products are used in the content. +- why these products are important to the application. + + + +This section should cover any additional details about the content that the first two parts missed. It should include: +- a detailed technical overview of the content. +- interesting facts about the content. +- any other relevant information that may be engaging to readers. + Follow these rules and guidelines when generating the report: @@ -26,12 +43,40 @@ Follow these rules and guidelines when generating the report: - The final Tweet/LinkedIn post will be developer focused, so ensure the report is technical and detailed. - Include any relevant links found in the content in the report. - Include details about what the product does/what problem it solves. -- Use proper markdown styling when formatting the report summary. +- Use proper markdown styling when formatting the marketing report. - If possible, keep the post at or under 280 characters (not including the URL) for conciseness. +- Generate the report in English, even if the content submitted is not in English. +Lastly, you should use the following process when writing the report: + +- First, read over the content VERY thoroughly. +- Take notes, and write down your thoughts about the content after reading it carefully. These should be interesting insights or facts which you think you'll need later on when writing the final report. This should be the first text you write. ALWAYS perform this step first, and wrap the notes and thoughts inside a "" tag. +- Finally, write the report. Use the notes and thoughts you wrote down in the previous step to help you write the report. This should be the second and last text you write. Wrap your report inside a "" tag. + + Do not include any personal opinions or biases in the report. Stick to the facts and technical details. -Your response should ONLY include the report summary, and no other text.`; +Your response should ONLY include the marketing report, and no other text. + +Given these instructions, examine the users input closely, and generate a marketing report on it.`; + +/** + * Parse the LLM generation to extract the report from inside the tag. + * If the report can not be parsed, the original generation is returned. + * @param generation The text generation to parse + * @returns The parsed generation, or the unmodified generation if it cannot be parsed + */ +function parseGeneration(generation: string): string { + const reportMatch = generation.match(/([\s\S]*?)<\/report>/); + if (!reportMatch) { + console.warn( + "Could not parse report from generation:\nSTART OF GENERATION\n\n", + generation, + "\n\nEND OF GENERATION", + ); + } + return reportMatch ? reportMatch[1].trim() : generation; +} const formatReportPrompt = (pageContents: string[]): string => { return `The following text contains summaries, or entire pages from the content I submitted to you. Please review the content and generate a report on it. @@ -61,6 +106,6 @@ export async function generateContentReport( ]); return { - report: result.content as string, + report: parseGeneration(result.content as string), }; } diff --git a/src/agent/subgraphs/generate-post/nodes/generate-post.ts b/src/agent/subgraphs/generate-post/nodes/generate-post.ts index 592d549..01db8fe 100644 --- a/src/agent/subgraphs/generate-post/nodes/generate-post.ts +++ b/src/agent/subgraphs/generate-post/nodes/generate-post.ts @@ -2,14 +2,7 @@ import { LangGraphRunnableConfig } from "@langchain/langgraph"; import { GraphAnnotation } from "../generate-post-state.js"; import { ChatAnthropic } from "@langchain/anthropic"; -const GENERATE_POST_PROMPT = `You are a highly regarded marketing employee at LangChain, working on crafting thoughtful and engaging content for LangChain's LinkedIn page. -You've been provided with a report on some content that you need to turn into a LinkedIn post. This content & report have been submitted to you by a third party who is looking to get their content promoted by LangChain. - -You should use the given report to generate an engaging, professional, and informative LinkedIn post that will be used to promote the content and LangChain's products. - -The following are examples of LinkedIn posts on third party content that have done well, and you should use them as inspiration for your post: - - +const TWEET_EXAMPLES = ` Podcastfy.ai 🎙️🤖 An Open Source API alternative to NotebookLM's podcast product @@ -37,30 +30,126 @@ RepoGPT is an open-source, AI-powered assistant Chat with your repositories using natural language to get insights, generate documentation, or receive code suggestions https://repogpt.com +`; + +const TWEET_EXAMPLES_BULLET_POINT = ` +🌐 Build agents that can interact with any website + +Check out this video by +@DendriteSystems + showing how to build an agent that can interact with websites just like a human would! + +This video demonstrates a workflow that: + +- Finds competitors on Product Hunt and Hacker News +- Drafts an email about new competitors +- Sends the email via Outlook + +📺 Video: https://youtube.com/watch?v=BGvqeRB4Jpk +🧠 Repo: https://github.com/dendrite-systems/dendrite-examples -Now that you've seen some examples, lets's cover the structure of the LinkedIn post you should follow: - -1. Post header. This should be a very short header, no more than 5 words that describes the content. This should ideally include one to two emojis, the name of the content provided (if applicable), and the LangChain product(s) the content uses. -2. Post body. This should be a two part body. The first part should be a concise, high level overview of the content and what it does, or aims to achieve. The second part should be a high level overview of how it uses LangChain's product(s) to do that. Ensure both of these are short, totally no more than 3 shorter sentences in total. Ensure these two parts are split with a newline between them. -3. Call to action. This should be a short sentence that encourages the reader to click the link to the content being promoted. This should include the link to the content being promoted. Optionally, you can include an emoji here. - + +🖋️AgentWrite LangGraph + +Features +- Automated content planning +- Paragraph-by-paragraph content generation +- Integration with multiple LLMs (OpenAI, GROQ, OLLaMA) +- Flexible workflow management using LangGraph +- Markdown output for generated content -This structure should ALWAYS be followed. And remember, the shorter and more engaging the post, the better (your bonus depends on this!!). +https://github.com/samwit/agent_tutorials/tree/main/agent_write + -Here are a set of rules and guidelines you should strictly follow when creating the LinkedIn post: + +✈️AI Travel Agent + +This is one of the most comprehensive examples we've seen of a LangGraph agent. It's specifically designed to be a real world practical use case + +Features +- Stateful Interactions +- Human-in-the-Loop +- Dynamic LLMs +- Email Automation + +https://github.com/nirbar1985/ai-travel-agent +`; + +const CONTENT_SUMMARY_PROMPT = `This section will contain the main content of the post. The post body should contain a concise, high-level overview of the content/product/service outlines in the marketing report. +It should focus on what the content does, or the problem it solves. Also include details on how the content implements LangChain's product(s) and why these products are important to the application. +Ensure this is short, no more than 3 sentences. You should NOT make the main focus of this on LangChain, but instead on the content itself. Remember, the content/product/service outlined in the marketing report is the main focus of this post.`; + +const BULLET_POINT_PROMPT = `This section will contain the main content of the post. The post body should contain 2-5 bullet points that cover the main points of the content/product/service outlines in the marketing report. +Each bullet point should have an emoji, and should be very concise, less than a single sentence. When thinking about what content to use for the bullet points, you should think from the point of view of a developer/CTO, and why they would benefit from the content. +Using this thinking, carefully craft your bullet points. At least one of these bullet points should cover how it uses LangChain's product(s). Remember, the content/product/service outlined in the marketing report is the main focus of this post.`; + +const BASE_GENERATE_POST_PROMPT = ({ + bulletPointStyle, +}: { + bulletPointStyle: boolean; +}) => `You're a highly regarded marketing employee at LangChain, working on crafting thoughtful and engaging content for LangChain's LinkedIn and Twitter pages. +You've been provided with a report on some content that you need to turn into a LinkedIn/Twitter post. The same post will be used for both platforms. +Your coworker has already taken the time to write a detailed marketing report on this content for you, so please take your time and read it carefully. + +The following are examples of LinkedIn/Twitter posts on third-party LangChain content that have done well, and you should use them as style inspiration for your post: + +${bulletPointStyle ? TWEET_EXAMPLES_BULLET_POINT : TWEET_EXAMPLES} + + +Now that you've seen some examples, lets's cover the structure of the LinkedIn/Twitter post you should follow. The post should have three main sections, outlined below: + +
+The first part of the post is the header. This should be very short, no more than 5 words, and should include one to two emojis, and the name of the content provided. If the marketing report does not specify a name, you should get creative and come up with a catchy title for it. +
+ +
+${bulletPointStyle ? BULLET_POINT_PROMPT : CONTENT_SUMMARY_PROMPT} +
+ +
+The final section of the post should contain a call to action. This should be a short sentence that encourages the reader to click the link to the content being promoted. Optionally, you can include an emoji here. +
+
+ +This structure should ALWAYS be followed. And remember, the shorter and more engaging the post, the better (your yearly bonus depends on this!!). + +Here are a set of rules and guidelines you should strictly follow when creating the LinkedIn/Twitter post: - Focus your post on what the content covers, aims to achieve, and how it uses LangChain's product(s) to do that. This should be concise and high level. -- Do not include technical details unless it is the entire focus of the content. +- Do not make the post over technical as some of our audience may not be advanced developers, but ensure it is technical enough to engage developers. - Keep posts short, concise and engaging -- Limit the use of emojis to the post header, and optionally in the call to action. +- Limit the use of emojis to the post header, ${bulletPointStyle ? "optionally in the call to action, and optionally in the bullet points." : "and optionally in the call to action."} - NEVER use hashtags in the post. -- Include the link to the content being promoted in the call to action section of the post. +- ALWAYS include the link to the content being promoted in the call to action section of the post. -Given these examples, rules, and the content provided by the user, curate a LinkedIn post that is engaging and follows the structure of the examples provided. - -Finally, ensure your response ONLY includes the LinkedIn post content, and does not include any additional information.`; +Lastly, you should follow the process below when writing the LinkedIn/Twitter post: + +Step 1. First, read over the marketing report VERY thoroughly. +Step 2. Take notes, and write down your thoughts about the report after reading it carefully. This should include details you think will help make the post more engaging, and your initial thoughts about what to focus the post on, the style, etc. This should be the first text you write. Wrap the notes and thoughts inside a "" tag. +Step 3. Lastly, write the LinkedIn/Twitter post. Use the notes and thoughts you wrote down in the previous step to help you write the post. This should be the last text you write. Wrap your report inside a "" tag. Ensure you write only ONE post for both LinkedIn and Twitter. + + +Given these examples, rules, and the content provided by the user, curate a LinkedIn/Twitter post that is engaging and follows the structure of the examples provided.`; + +/** + * Parse the LLM generation to extract the report from inside the tag. + * If the report can not be parsed, the original generation is returned. + * @param generation The text generation to parse + * @returns The parsed generation, or the unmodified generation if it cannot be parsed + */ +function parseGeneration(generation: string): string { + const reportMatch = generation.match(/([\s\S]*?)<\/post>/); + if (!reportMatch) { + console.warn( + "Could not parse post from generation:\nSTART OF POST GENERATION\n\n", + generation, + "\n\nEND OF POST GENERATION", + ); + } + return reportMatch ? reportMatch[1].trim() : generation; +} const formatPrompt = (report: string, link: string) => { return `Here is the report I wrote on the content I'd like promoted by LangChain: @@ -86,23 +175,38 @@ export async function generatePosts( } const postModel = new ChatAnthropic({ model: "claude-3-5-sonnet-20241022", - temperature: 0, + temperature: 0.5, }); const prompt = formatPrompt(state.report, state.relevantLinks[0]); - const result = await postModel.invoke([ - { - role: "system", - content: GENERATE_POST_PROMPT, - }, - { - role: "user", - content: prompt, - }, + const [summaryPost, bulletPointsPost] = await Promise.all([ + postModel.invoke([ + { + role: "system", + content: BASE_GENERATE_POST_PROMPT({ bulletPointStyle: false }), + }, + { + role: "user", + content: prompt, + }, + ]), + postModel.invoke([ + { + role: "system", + content: BASE_GENERATE_POST_PROMPT({ bulletPointStyle: true }), + }, + { + role: "user", + content: prompt, + }, + ]), ]); return { - post: result.content as string, + posts: [ + parseGeneration(summaryPost.content as string), + parseGeneration(bulletPointsPost.content as string), + ], }; } diff --git a/src/agent/subgraphs/shared/nodes/verify-github.ts b/src/agent/subgraphs/shared/nodes/verify-github.ts index 074521b..2958e9f 100644 --- a/src/agent/subgraphs/shared/nodes/verify-github.ts +++ b/src/agent/subgraphs/shared/nodes/verify-github.ts @@ -48,14 +48,10 @@ const tryGetReadmeContents = async ( try { const response = await fetch(url); if (!response.ok) { - console.log("Failed to fetch URL", url); - } else { - content = await response.text(); - if (content) { - console.log("got content from url", url); - console.log(content); - } + return undefined; } + + content = await response.text(); } catch (_) { // no-op } @@ -86,7 +82,6 @@ export async function getGitHubContentsAndTypeFromUrl( console.error("Failed to parse GitHub URL", e); return undefined; } - console.log("baseGitHubRepoUrl", baseGitHubRepoUrl); if (hasFileExtension(url) && messageAttachments) { // Use the `text` field of the attachment as the content @@ -99,10 +94,8 @@ export async function getGitHubContentsAndTypeFromUrl( const rawMasterReadmeLinkLowercase = `https://raw.githubusercontent.com${baseGitHubRepoUrl}/refs/heads/master/readme.md`; // Attempt to fetch the contents of main, if it fails, try master, finally, just read the content of the original URL. pageContent = await tryGetReadmeContents([ - // rawMainReadmeLink, - // rawMainReadmeLinkLowercase, - rawMainReadmeLinkLowercase, rawMainReadmeLink, + rawMainReadmeLinkLowercase, rawMasterReadmeLink, rawMasterReadmeLinkLowercase, url, @@ -122,6 +115,7 @@ export async function getGitHubContentsAndTypeFromUrl( export async function verifyGitHubContentIsRelevant( contents: string, fileType: string, + config: LangGraphRunnableConfig, ): Promise { const relevancyModel = new ChatAnthropic({ model: "claude-3-5-sonnet-20241022", @@ -134,19 +128,22 @@ export async function verifyGitHubContentIsRelevant( .withConfig({ runName: "check-github-relevancy-model", }) - .invoke([ - { - role: "system", - content: VERIFY_LANGCHAIN_RELEVANT_CONTENT_PROMPT.replaceAll( - "{file_type}", - fileType, - ), - }, - { - role: "user", - content: contents, - }, - ]); + .invoke( + [ + { + role: "system", + content: VERIFY_LANGCHAIN_RELEVANT_CONTENT_PROMPT.replaceAll( + "{file_type}", + fileType, + ), + }, + { + role: "user", + content: contents, + }, + ], + config, + ); return relevant; } @@ -155,13 +152,14 @@ export async function verifyGitHubContentIsRelevant( */ export async function verifyGitHubContent( state: typeof VerifyContentAnnotation.State, - _config: LangGraphRunnableConfig, + config: LangGraphRunnableConfig, ): Promise { const contentsAndType = await getGitHubContentsAndTypeFromUrl( state.link, state.slackMessage.attachments?.[0].text, ); if (!contentsAndType) { + console.warn("No contents found for GitHub URL", state.link); return { relevantLinks: [], pageContents: [], @@ -171,15 +169,16 @@ export async function verifyGitHubContent( const relevant = await verifyGitHubContentIsRelevant( contentsAndType.contents, contentsAndType.fileType, + config, ); if (relevant) { return { - // TODO: Replace with actual relevant link/page content (summary in this case) relevantLinks: [state.link], pageContents: [contentsAndType.contents], }; } + console.log("Content is not relevant", state.link); // Not relevant, return empty arrays so this URL is not included. return { relevantLinks: [], diff --git a/src/agent/subgraphs/shared/nodes/verify-youtube.ts b/src/agent/subgraphs/shared/nodes/verify-youtube.ts index a9791e6..63c175c 100644 --- a/src/agent/subgraphs/shared/nodes/verify-youtube.ts +++ b/src/agent/subgraphs/shared/nodes/verify-youtube.ts @@ -6,6 +6,7 @@ import { ChatAnthropic } from "@langchain/anthropic"; import { HumanMessage } from "@langchain/core/messages"; import { LANGCHAIN_PRODUCTS_CONTEXT } from "../../generate-post/prompts.js"; import { VerifyContentAnnotation } from "../shared-state.js"; +import { getYouTubeVideoDuration } from "./youtube.utils.js"; type VerifyYouTubeContentReturn = { relevantLinks: (typeof GraphAnnotation.State)["relevantLinks"]; @@ -113,6 +114,21 @@ export async function verifyYouTubeContent( state: typeof VerifyContentAnnotation.State, _config: LangGraphRunnableConfig, ): Promise { + const videoDurationS = await getYouTubeVideoDuration(state.link); + + if (videoDurationS === undefined) { + // TODO: Handle this better + throw new Error("Failed to get video duration"); + } + + // 1800 = 30 minutes + if (videoDurationS > 1800) { + // TODO: Replace with interrupt requesting user confirm if they want to continue + throw new Error( + "Video is longer than 30 minutes, please confirm you want to continue.", + ); + } + const videoSummary = await generateVideoSummary(state.link); const relevant = await verifyYouTubeContentIsRelevant(videoSummary); diff --git a/src/agent/subgraphs/shared/nodes/youtube.utils.ts b/src/agent/subgraphs/shared/nodes/youtube.utils.ts new file mode 100644 index 0000000..30e5664 --- /dev/null +++ b/src/agent/subgraphs/shared/nodes/youtube.utils.ts @@ -0,0 +1,81 @@ +import { youtube } from "@googleapis/youtube"; +import { GoogleAuth } from "google-auth-library"; + +/** + * Extracts the videoId from a YouTube video URL. + * @param url The URL of the YouTube video. + * @returns The videoId of the YouTube video. + */ +function getVideoID(url: string): string | undefined { + const match = url.match( + /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=)([^#&?]*).*/, + ); + if (match !== null && match[1].length === 11) { + return match[1]; + } else { + return undefined; + } +} + +/** + * Converts ISO 8601 duration to seconds + * @param duration ISO 8601 duration string (e.g., "PT15M51S") + * @returns number of seconds + */ +function parseDuration(duration: string): number { + const match = duration.match(/PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/); + if (!match) return 0; + + const hours = parseInt(match[1] || "0"); + const minutes = parseInt(match[2] || "0"); + const seconds = parseInt(match[3] || "0"); + + return hours * 3600 + minutes * 60 + seconds; +} + +/** + * Get the duration of a video from a YouTube URL. + * @param videoUrl The URL of the YouTube video + * @returns The duration of the video in seconds + */ +export async function getYouTubeVideoDuration( + videoUrl: string, +): Promise { + const videoId = getVideoID(videoUrl); + if (!videoId) return undefined; + if (!process.env.GOOGLE_VERTEX_AI_WEB_CREDENTIALS) { + throw new Error("GOOGLE_VERTEX_AI_WEB_CREDENTIALS is not set"); + } + const parsedGoogleCredentials = JSON.parse( + process.env.GOOGLE_VERTEX_AI_WEB_CREDENTIALS, + ); + + const auth = new GoogleAuth({ + credentials: parsedGoogleCredentials, + scopes: ["https://www.googleapis.com/auth/youtube.readonly"], + }); + + const youtubeClient = youtube({ + version: "v3", + auth, + }); + + const videoInfo = await youtubeClient.videos.list({ + id: [videoId], + part: ["contentDetails"], // Add this to get duration info + }); + + if (!videoInfo.data.items?.length || videoInfo.data.items?.length > 1) { + // TODO: Handle this better + throw new Error(`Expected 1 item, got ${videoInfo.data.items?.length}`); + } + + let videoDuration: number | undefined = undefined; + videoInfo.data.items?.forEach((i) => { + const duration = i.contentDetails?.duration; + if (duration) { + videoDuration = parseDuration(duration); + } + }); + return videoDuration; +} diff --git a/src/clients/slack.ts b/src/clients/slack.ts index 46afebb..1669bf9 100644 --- a/src/clients/slack.ts +++ b/src/clients/slack.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-process-env */ import { WebClient, ConversationsHistoryResponse } from "@slack/web-api"; import moment from "moment"; @@ -102,8 +101,7 @@ export class SlackMessageFetcher { } try { - // const oldest = moment().subtract(24, 'hours').unix().toString(); - const oldest = moment().subtract(1, "month").unix().toString(); + const oldest = moment().subtract(24, "hours").unix().toString(); const messages: SlackMessage[] = []; let cursor: string | undefined; diff --git a/src/evals/general/index.ts b/src/evals/general/index.ts new file mode 100644 index 0000000..f5b7999 --- /dev/null +++ b/src/evals/general/index.ts @@ -0,0 +1,42 @@ +import { type Example, Run } from "langsmith"; +import { evaluate, EvaluationResult } from "langsmith/evaluation"; +// eslint-disable-next-line import/no-extraneous-dependencies +import "dotenv/config"; +import { generatePostGraph } from "../../agent/subgraphs/generate-post/graph.js"; + +const runGraph = async ( + input: Record, +): Promise> => { + return await generatePostGraph.invoke(input); +}; + +const evaluatePost = (run: Run, example?: Example): EvaluationResult => { + if (!example) { + throw new Error("No example provided"); + } + if (!example.outputs) { + throw new Error("No example outputs provided"); + } + if (!run.outputs) { + throw new Error("No run outputs provided"); + } + + // TODO: Implement evaluation logic + throw new Error("Evaluation logic not implemented"); +}; + +async function runEval() { + const datasetName = "sma:generate-post:general"; + await evaluate(runGraph, { + data: datasetName, + evaluators: [evaluatePost], + experimentPrefix: "Post Generation-General", + }); +} + +runEval().catch(console.error); + +// https://x.com/LangChainAI/status/1858311912091476455 +// https://x.com/LangChainAI/status/1857811436984217835 +// https://x.com/LangChainAI/status/1856026604180242636 +// https://x.com/LangChainAI/status/1855437724536504482 diff --git a/src/evals/github/index.ts b/src/evals/github/index.ts new file mode 100644 index 0000000..7469786 --- /dev/null +++ b/src/evals/github/index.ts @@ -0,0 +1,52 @@ +import { type Example, Run } from "langsmith"; +import { evaluate, EvaluationResult } from "langsmith/evaluation"; +// eslint-disable-next-line import/no-extraneous-dependencies +import "dotenv/config"; +import { generatePostGraph } from "../../agent/subgraphs/generate-post/graph.js"; + +const runGraph = async ( + input: Record, +): Promise> => { + return await generatePostGraph.invoke(input); +}; + +const evaluatePost = (run: Run, example?: Example): EvaluationResult => { + if (!example) { + throw new Error("No example provided"); + } + if (!example.outputs) { + throw new Error("No example outputs provided"); + } + if (!run.outputs) { + throw new Error("No run outputs provided"); + } + console.log("\n\nGENERATED POST:\n", run.outputs.posts.join("\n---\n")); + console.log("\nEXAMPLE POST:\n", example.outputs.post); + + return { + key: "correct_generation", + score: true, + }; +}; + +async function runEval() { + const datasetName = "sma:generate-post:github"; + await evaluate(runGraph, { + data: datasetName, + evaluators: [evaluatePost], + experimentPrefix: "Post Generation-Github", + }); +} + +runEval().catch(console.error); + +// Should be approved and posts generated +// https://x.com/LangChainAI/status/1861108590792036799 +// https://x.com/LangChainAI/status/1860760295188185246 +// https://x.com/LangChainAI/status/1860745200668201148 +// https://x.com/LangChainAI/status/1860714493661106562 +// https://x.com/LangChainAI/status/1860485484683911584 +// https://x.com/LangChainAI/status/1860397908451033240 + +// Would need review: +// https://x.com/LangChainAI/status/1858175010612916272 diff --git a/src/evals/twitter/index.ts b/src/evals/twitter/index.ts new file mode 100644 index 0000000..22886f4 --- /dev/null +++ b/src/evals/twitter/index.ts @@ -0,0 +1,37 @@ +import { type Example, Run } from "langsmith"; +import { evaluate, EvaluationResult } from "langsmith/evaluation"; +// eslint-disable-next-line import/no-extraneous-dependencies +import "dotenv/config"; +import { generatePostGraph } from "../../agent/subgraphs/generate-post/graph.js"; + +const runGraph = async ( + input: Record, +): Promise> => { + return await generatePostGraph.invoke(input); +}; + +const evaluatePost = (run: Run, example?: Example): EvaluationResult => { + if (!example) { + throw new Error("No example provided"); + } + if (!example.outputs) { + throw new Error("No example outputs provided"); + } + if (!run.outputs) { + throw new Error("No run outputs provided"); + } + + // TODO: Implement evaluation logic + throw new Error("Evaluation logic not implemented"); +}; + +async function runEval() { + const datasetName = "sma:generate-post:twitter"; + await evaluate(runGraph, { + data: datasetName, + evaluators: [evaluatePost], + experimentPrefix: "Post Generation-Twitter", + }); +} + +runEval().catch(console.error); diff --git a/src/evals/youtube/index.ts b/src/evals/youtube/index.ts new file mode 100644 index 0000000..208115b --- /dev/null +++ b/src/evals/youtube/index.ts @@ -0,0 +1,43 @@ +import { type Example, Run } from "langsmith"; +import { evaluate, EvaluationResult } from "langsmith/evaluation"; +// eslint-disable-next-line import/no-extraneous-dependencies +import "dotenv/config"; +import { generatePostGraph } from "../../agent/subgraphs/generate-post/graph.js"; + +const runGraph = async ( + input: Record, +): Promise> => { + return await generatePostGraph.invoke(input); +}; + +const evaluatePost = (run: Run, example?: Example): EvaluationResult => { + if (!example) { + throw new Error("No example provided"); + } + if (!example.outputs) { + throw new Error("No example outputs provided"); + } + if (!run.outputs) { + throw new Error("No run outputs provided"); + } + + // TODO: Implement evaluation logic + throw new Error("Evaluation logic not implemented"); +}; + +async function runEval() { + const datasetName = "sma:generate-post:youtube"; + await evaluate(runGraph, { + data: datasetName, + evaluators: [evaluatePost], + experimentPrefix: "Post Generation-YouTube", + }); +} + +runEval().catch(console.error); + +// https://x.com/LangChainAI/status/1860438927892709871 +// https://x.com/LangChainAI/status/1860352611834069056 +// https://x.com/LangChainAI/status/1855629502690349326 +// https://x.com/LangChainAI/status/1855362227420967092 +// https://x.com/LangChainAI/status/1854925528031195148 diff --git a/src/tests/graph.int.test.ts b/src/tests/graph.int.test.ts index bb542d4..e0c605f 100644 --- a/src/tests/graph.int.test.ts +++ b/src/tests/graph.int.test.ts @@ -1,10 +1,15 @@ import { describe, it } from "@jest/globals"; import { generatePostGraph } from "../agent/subgraphs/generate-post/graph.js"; -import { GITHUB_URL_STATE, TWITTER_NESTED_GITHUB_MESSAGE } from "./states.js"; +import { + GITHUB_MESSAGE, + GITHUB_URL_STATE, + TWITTER_NESTED_GITHUB_MESSAGE, +} from "./states.js"; import { TwitterApi } from "twitter-api-v2"; import { resolveTwitterUrl } from "../agent/subgraphs/verify-tweet/utils.js"; import { getGitHubContentsAndTypeFromUrl } from "../agent/subgraphs/shared/nodes/verify-github.js"; import { EXPECTED_README } from "./expected.js"; +import { getYouTubeVideoDuration } from "../agent/subgraphs/shared/nodes/youtube.utils.js"; describe.skip("GeneratePostGraph", () => { it.skip("Should be able to generate posts from a GitHub URL slack message", async () => { @@ -60,7 +65,7 @@ test("Can get the proper markdown from a github URL", async () => { expect(contents.contents).toBe(EXPECTED_README); }); -describe.only("generate via twitter posts", () => { +describe("generate via twitter posts", () => { it("Can generate a post from a tweet with a github link", async () => { console.log("Starting graph test"); const result = await generatePostGraph.stream( @@ -90,3 +95,38 @@ describe.only("generate via twitter posts", () => { } }, 60000); }); + +describe("generate via github repos", () => { + it("Can generate a post from a github repo", async () => { + console.log("Starting graph test"); + const result = await generatePostGraph.stream(GITHUB_MESSAGE, { + streamMode: "values", + }); + + let post = ""; + for await (const value of result) { + console.log( + "Event occurred", + Object.entries(value).map(([k, v]) => ({ + [k]: !!v, + })), + ); + + if (value.post) { + post = value.post; + } + } + + if (post) { + console.log("\nPOST:\n"); + console.log(post); + } + }, 60000); +}); + +test("Can get video duration", async () => { + const duration = await getYouTubeVideoDuration( + "https://www.youtube.com/watch?v=BGvqeRB4Jpk", + ); + expect(duration).toBe(91); +}); diff --git a/src/tests/states.ts b/src/tests/states.ts index bc8d921..1a54261 100644 --- a/src/tests/states.ts +++ b/src/tests/states.ts @@ -72,3 +72,14 @@ export const TWITTER_NESTED_GENERAL_MESSAGE = { links: ["https://x.com/KaranVaidya6/status/1861037496295137314"], }, }; + +export const GITHUB_MESSAGE = { + slackMessage: { + id: "e4fd6d66-7a47-4457-b532-14facfe93bb0", + timestamp: "1729953413.037949", + user: "U04N0HGF869", + text: "", + type: "message", + links: ["https://github.com/starpig1129/AI-Data-Analysis-MultiAgent"], + }, +}; diff --git a/yarn.lock b/yarn.lock index b99b201..b284a17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -304,6 +304,126 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@esbuild/aix-ppc64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz#51299374de171dbd80bb7d838e1cfce9af36f353" + integrity sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ== + +"@esbuild/android-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz#58565291a1fe548638adb9c584237449e5e14018" + integrity sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw== + +"@esbuild/android-arm@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.23.1.tgz#5eb8c652d4c82a2421e3395b808e6d9c42c862ee" + integrity sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ== + +"@esbuild/android-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.23.1.tgz#ae19d665d2f06f0f48a6ac9a224b3f672e65d517" + integrity sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg== + +"@esbuild/darwin-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz#05b17f91a87e557b468a9c75e9d85ab10c121b16" + integrity sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q== + +"@esbuild/darwin-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz#c58353b982f4e04f0d022284b8ba2733f5ff0931" + integrity sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw== + +"@esbuild/freebsd-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz#f9220dc65f80f03635e1ef96cfad5da1f446f3bc" + integrity sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA== + +"@esbuild/freebsd-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz#69bd8511fa013b59f0226d1609ac43f7ce489730" + integrity sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g== + +"@esbuild/linux-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz#8050af6d51ddb388c75653ef9871f5ccd8f12383" + integrity sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g== + +"@esbuild/linux-arm@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz#ecaabd1c23b701070484990db9a82f382f99e771" + integrity sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ== + +"@esbuild/linux-ia32@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz#3ed2273214178109741c09bd0687098a0243b333" + integrity sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ== + +"@esbuild/linux-loong64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz#a0fdf440b5485c81b0fbb316b08933d217f5d3ac" + integrity sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw== + +"@esbuild/linux-mips64el@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz#e11a2806346db8375b18f5e104c5a9d4e81807f6" + integrity sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q== + +"@esbuild/linux-ppc64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz#06a2744c5eaf562b1a90937855b4d6cf7c75ec96" + integrity sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw== + +"@esbuild/linux-riscv64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz#65b46a2892fc0d1af4ba342af3fe0fa4a8fe08e7" + integrity sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA== + +"@esbuild/linux-s390x@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz#e71ea18c70c3f604e241d16e4e5ab193a9785d6f" + integrity sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw== + +"@esbuild/linux-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz#d47f97391e80690d4dfe811a2e7d6927ad9eed24" + integrity sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ== + +"@esbuild/netbsd-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz#44e743c9778d57a8ace4b72f3c6b839a3b74a653" + integrity sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA== + +"@esbuild/openbsd-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz#05c5a1faf67b9881834758c69f3e51b7dee015d7" + integrity sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q== + +"@esbuild/openbsd-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz#2e58ae511bacf67d19f9f2dcd9e8c5a93f00c273" + integrity sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA== + +"@esbuild/sunos-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz#adb022b959d18d3389ac70769cef5a03d3abd403" + integrity sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA== + +"@esbuild/win32-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz#84906f50c212b72ec360f48461d43202f4c8b9a2" + integrity sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A== + +"@esbuild/win32-ia32@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz#5e3eacc515820ff729e90d0cb463183128e82fac" + integrity sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ== + +"@esbuild/win32-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz#81fd50d11e2c32b2d6241470e3185b70c7b30699" + integrity sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg== + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -356,6 +476,13 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.9.1.tgz#4a97e85e982099d6c7ee8410aacb55adaa576f06" integrity sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ== +"@googleapis/youtube@^20.0.0": + version "20.0.0" + resolved "https://registry.yarnpkg.com/@googleapis/youtube/-/youtube-20.0.0.tgz#5bcf8473e34ed852bac76ae2f2764dac9e0e58e9" + integrity sha512-wdt1J0JoKYhvpoS2XIRHX0g/9ul/B0fQeeJAhuuBIdYINuuLt6/oZYZZCBmkuhtkA3IllXgqgAXOjLtLRAnR2g== + dependencies: + googleapis-common "^7.0.0" + "@humanwhocodes/config-array@^0.11.14": version "0.11.14" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" @@ -1055,6 +1182,13 @@ acorn@^8.12.0, acorn@^8.9.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== +agent-base@^7.0.2: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + agentkeepalive@^4.2.1: version "4.5.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" @@ -1288,11 +1422,16 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.5.1: +base64-js@^1.3.0, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +bignumber.js@^9.0.0: + version "9.1.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" + integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== + binary-extensions@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" @@ -1349,6 +1488,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -1576,6 +1720,13 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" +debug@4: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -1699,6 +1850,13 @@ dotenv@^16.4.5: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== +ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + ejs@^3.1.10: version "3.1.10" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" @@ -1837,6 +1995,36 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +esbuild@~0.23.0: + version "0.23.1" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.23.1.tgz#40fdc3f9265ec0beae6f59824ade1bd3d3d2dab8" + integrity sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg== + optionalDependencies: + "@esbuild/aix-ppc64" "0.23.1" + "@esbuild/android-arm" "0.23.1" + "@esbuild/android-arm64" "0.23.1" + "@esbuild/android-x64" "0.23.1" + "@esbuild/darwin-arm64" "0.23.1" + "@esbuild/darwin-x64" "0.23.1" + "@esbuild/freebsd-arm64" "0.23.1" + "@esbuild/freebsd-x64" "0.23.1" + "@esbuild/linux-arm" "0.23.1" + "@esbuild/linux-arm64" "0.23.1" + "@esbuild/linux-ia32" "0.23.1" + "@esbuild/linux-loong64" "0.23.1" + "@esbuild/linux-mips64el" "0.23.1" + "@esbuild/linux-ppc64" "0.23.1" + "@esbuild/linux-riscv64" "0.23.1" + "@esbuild/linux-s390x" "0.23.1" + "@esbuild/linux-x64" "0.23.1" + "@esbuild/netbsd-x64" "0.23.1" + "@esbuild/openbsd-arm64" "0.23.1" + "@esbuild/openbsd-x64" "0.23.1" + "@esbuild/sunos-x64" "0.23.1" + "@esbuild/win32-arm64" "0.23.1" + "@esbuild/win32-ia32" "0.23.1" + "@esbuild/win32-x64" "0.23.1" + escalade@^3.1.1, escalade@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" @@ -2086,6 +2274,11 @@ expr-eval@^2.0.2: resolved "https://registry.yarnpkg.com/expr-eval/-/expr-eval-2.0.2.tgz#fa6f044a7b0c93fde830954eb9c5b0f7fbc7e201" integrity sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg== +extend@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -2233,7 +2426,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2: +fsevents@^2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -2258,6 +2451,25 @@ functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +gaxios@^6.0.0, gaxios@^6.0.3, gaxios@^6.1.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-6.7.1.tgz#ebd9f7093ede3ba502685e73390248bb5b7f71fb" + integrity sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ== + dependencies: + extend "^3.0.2" + https-proxy-agent "^7.0.1" + is-stream "^2.0.0" + node-fetch "^2.6.9" + uuid "^9.0.1" + +gcp-metadata@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-6.1.0.tgz#9b0dd2b2445258e7597f2024332d20611cbd6b8c" + integrity sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg== + dependencies: + gaxios "^6.0.0" + json-bigint "^1.0.0" + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -2298,6 +2510,13 @@ get-symbol-description@^1.0.2: es-errors "^1.3.0" get-intrinsic "^1.2.4" +get-tsconfig@^4.7.5: + version "4.8.1" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.8.1.tgz#8995eb391ae6e1638d251118c7b56de7eb425471" + integrity sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg== + dependencies: + resolve-pkg-maps "^1.0.0" + glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -2361,6 +2580,30 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +google-auth-library@^9.15.0, google-auth-library@^9.7.0: + version "9.15.0" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-9.15.0.tgz#1b009c08557929c881d72f953f17e839e91b009b" + integrity sha512-7ccSEJFDFO7exFbO6NRyC+xH8/mZ1GZGG2xxx9iHxZWcjUjJpjWxIMw3cofAKcueZ6DATiukmmprD7yavQHOyQ== + dependencies: + base64-js "^1.3.0" + ecdsa-sig-formatter "^1.0.11" + gaxios "^6.1.1" + gcp-metadata "^6.1.0" + gtoken "^7.0.0" + jws "^4.0.0" + +googleapis-common@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/googleapis-common/-/googleapis-common-7.2.0.tgz#5c19102c9af1e5d27560be5e69ee2ccf68755d42" + integrity sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA== + dependencies: + extend "^3.0.2" + gaxios "^6.0.3" + google-auth-library "^9.7.0" + qs "^6.7.0" + url-template "^2.0.8" + uuid "^9.0.0" + gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -2378,6 +2621,14 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== +gtoken@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-7.1.0.tgz#d61b4ebd10132222817f7222b1e6064bd463fc26" + integrity sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw== + dependencies: + gaxios "^6.0.0" + jws "^4.0.0" + has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" @@ -2439,6 +2690,14 @@ htmlparser2@^9.1.0: domutils "^3.1.0" entities "^4.5.0" +https-proxy-agent@^7.0.1: + version "7.0.5" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" + integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== + dependencies: + agent-base "^7.0.2" + debug "4" + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -3132,6 +3391,13 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +json-bigint@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" + integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== + dependencies: + bignumber.js "^9.0.0" + json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -3169,6 +3435,23 @@ jsonpointer@^5.0.1: resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== +jwa@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" + integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" + integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== + dependencies: + jwa "^2.0.0" + safe-buffer "^5.0.1" + keyv@^4.5.3: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -3199,7 +3482,7 @@ kleur@^3.0.3: zod "^3.22.4" zod-to-json-schema "^3.22.3" -langsmith@^0.2.0: +langsmith@^0.2.0, langsmith@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.2.7.tgz#db1f83f90c780049dcc55d076a5c161c4f14b8c4" integrity sha512-9LFOp30cQ9K/7rzMt4USBI0SEKKhsH4l42ZERBPXOmDXnR5gYpsGFw8SZR0A6YLnc6vvoEmtr/XKel0Odq2UWw== @@ -3343,7 +3626,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0, ms@^2.1.1: +ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -3368,7 +3651,7 @@ node-domexception@1.0.0: resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== -node-fetch@^2.6.7: +node-fetch@^2.6.7, node-fetch@^2.6.9: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== @@ -3697,6 +3980,13 @@ pure-rand@^6.0.0: resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== +qs@^6.7.0: + version "6.13.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.1.tgz#3ce5fc72bd3a8171b85c99b93c65dd20b7d1b16e" + integrity sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg== + dependencies: + side-channel "^1.0.6" + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -3739,6 +4029,11 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + resolve.exports@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" @@ -3792,6 +4087,11 @@ safe-array-concat@^1.1.2: has-symbols "^1.0.3" isarray "^2.0.5" +safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-regex-test@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" @@ -3850,7 +4150,7 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -side-channel@^1.0.4: +side-channel@^1.0.4, side-channel@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== @@ -4076,6 +4376,16 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" +tsx@^4.19.2: + version "4.19.2" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.19.2.tgz#2d7814783440e0ae42354d0417d9c2989a2ae92c" + integrity sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g== + dependencies: + esbuild "~0.23.0" + get-tsconfig "^4.7.5" + optionalDependencies: + fsevents "~2.3.3" + twitter-api-v2@^1.18.2: version "1.18.2" resolved "https://registry.yarnpkg.com/twitter-api-v2/-/twitter-api-v2-1.18.2.tgz#fd03d0ac4c7ed9f0d8f76786d5908d7aced3af78" @@ -4192,6 +4502,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-template@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21" + integrity sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw== + uuid@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294"