Added youtube search tool/block

This commit is contained in:
Waleed Latif
2025-02-06 12:48:41 -08:00
parent bc5dba714e
commit 5c34f82d91
6 changed files with 174 additions and 30 deletions

57
blocks/blocks/youtube.ts Normal file
View File

@@ -0,0 +1,57 @@
import { YouTubeIcon } from '@/components/icons'
import { YouTubeSearchResponse } from '@/tools/youtube/search'
import { BlockConfig } from '../types'
export const YouTubeSearchBlock: BlockConfig<YouTubeSearchResponse> = {
type: 'youtube_search',
toolbar: {
title: 'YouTube Search',
description: 'Search for videos on YouTube',
bgColor: '#FF0000',
icon: YouTubeIcon,
category: 'tools',
},
tools: {
access: ['youtube_search'],
},
workflow: {
inputs: {
apiKey: { type: 'string', required: true },
query: { type: 'string', required: true },
maxResults: { type: 'number', required: false },
},
outputs: {
response: {
type: {
items: 'json',
totalResults: 'number',
},
},
},
subBlocks: [
{
id: 'query',
title: 'Search Query',
type: 'short-input',
layout: 'full',
placeholder: 'Enter search query',
},
{
id: 'apiKey',
title: 'YouTube API Key',
type: 'short-input',
layout: 'full',
placeholder: 'Enter YouTube API Key',
password: true,
},
{
id: 'maxResults',
title: 'Max Results',
type: 'slider',
layout: 'half',
min: 0,
max: 20,
},
],
},
}

View File

@@ -12,6 +12,7 @@ import { SerperBlock } from './blocks/serper'
import { SlackMessageBlock } from './blocks/slack'
import { TavilyExtractBlock, TavilySearchBlock } from './blocks/tavily'
import { TranslateBlock } from './blocks/translate'
import { YouTubeSearchBlock } from './blocks/youtube'
import { BlockConfig } from './types'
// Export blocks for ease of use
@@ -30,6 +31,7 @@ export {
TavilySearchBlock,
TavilyExtractBlock,
RouterBlock,
YouTubeSearchBlock,
}
// Registry of all block configurations
@@ -48,6 +50,7 @@ const blocks: Record<string, BlockConfig> = {
serper_search: SerperBlock,
tavily_search: TavilySearchBlock,
tavily_extract: TavilyExtractBlock,
youtube_search: YouTubeSearchBlock,
}
// Build a reverse mapping of tools to block types

View File

@@ -1124,4 +1124,19 @@ export const ConnectIcon = (props: SVGProps<SVGSVGElement>) => (
fill="currentColor"
/>
</svg>
);
)
export function YouTubeIcon(props: React.SVGProps<SVGSVGElement>) {
return (
<svg
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="#FFFFFF"
{...props}
>
<title>YouTube</title>
<path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" />
</svg>
)
}

View File

@@ -21,6 +21,12 @@ import { cn } from '@/lib/utils'
// This file is not typed correctly from shadcn, so we're disabling the type checker
// @ts-nocheck
// This file is not typed correctly from shadcn, so we're disabling the type checker
// @ts-nocheck
// This file is not typed correctly from shadcn, so we're disabling the type checker
// @ts-nocheck
const Command = React.forwardRef<
React.ElementRef<typeof CommandPrimitive>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive> & {

View File

@@ -17,36 +17,9 @@ import { extractTool as tavilyExtract } from './tavily/extract'
import { searchTool as tavilySearch } from './tavily/search'
import { ToolConfig, ToolResponse } from './types'
import { chatTool as xaiChat } from './xai/chat'
import { youtubeSearchTool } from './youtube/search'
/**
* Registry of all available tools. Each tool is designed for specific use cases:
*
* AI Model Tools:
* - openai_chat: Advanced language model for general conversation, reasoning, and task completion
* - anthropic_chat: Claude model specialized in detailed analysis and complex reasoning
* - google_chat: PaLM model for general conversation and task assistance
* - xai_chat: Specialized AI model for explainable AI interactions
* - deepseek_chat: Code-specialized language model for programming tasks
* - deepseek_reasoner: Advanced reasoning model for complex problem-solving
*
* Web & Data Tools:
* - http_request: Make HTTP requests to any API endpoint with custom headers and body
* - firecrawl_scrape: Extract structured data from web pages with advanced scraping
* - jina_readurl: Efficiently read and parse content from web URLs
* - serper_search: Perform web searches with high-quality, structured results
* - tavily_search: AI-powered web search with comprehensive results
* - tavily_extract: Extract specific information from web content
*
* Business Integration Tools:
* - hubspot_contacts: Manage and query HubSpot CRM contacts
* - salesforce_opportunities: Handle Salesforce sales opportunities
* - slack_message: Send messages to Slack channels or users
* - github_repoinfo: Fetch detailed information about GitHub repositories
*
* Utility Tools:
* - function_execute: Execute custom JavaScript functions with provided parameters
* - crewai_vision: Process and analyze images with AI capabilities
*/
// Registry of all available tools
export const tools: Record<string, ToolConfig> = {
// AI Models
openai_chat: openAIChat,
@@ -76,6 +49,7 @@ export const tools: Record<string, ToolConfig> = {
serper_search: serperSearch,
tavily_search: tavilySearch,
tavily_extract: tavilyExtract,
youtube_search: youtubeSearchTool,
}
// Get a tool by its ID

89
tools/youtube/search.ts Normal file
View File

@@ -0,0 +1,89 @@
import { ToolConfig, ToolResponse } from '../types'
export interface YouTubeSearchParams {
apiKey: string
query: string
maxResults?: number
pageToken?: string
}
export interface YouTubeSearchResponse extends ToolResponse {
output: {
items: Array<{
videoId: string
title: string
description: string
thumbnail: string
}>
totalResults: number
nextPageToken?: string
}
}
export const youtubeSearchTool: ToolConfig<YouTubeSearchParams, YouTubeSearchResponse> = {
id: 'youtube_search',
name: 'YouTube Search',
description: 'Search for videos on YouTube using the YouTube Data API.',
version: '1.0.0',
params: {
query: {
type: 'string',
required: true,
description: 'Search query for YouTube videos',
},
apiKey: {
type: 'string',
required: true,
requiredForToolCall: true,
description: 'YouTube API Key',
},
maxResults: {
type: 'number',
required: false,
default: 5,
description: 'Maximum number of videos to return',
},
},
request: {
url: (params: YouTubeSearchParams) => {
let url = `https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&key=${params.apiKey}&q=${encodeURIComponent(
params.query
)}`
url += `&maxResults=${params.maxResults || 5}`
return url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response): Promise<YouTubeSearchResponse> => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'YouTube API error')
}
const items = (data.items || []).map((item: any) => ({
videoId: item.id?.videoId,
title: item.snippet?.title,
description: item.snippet?.description,
thumbnail:
item.snippet?.thumbnails?.default?.url ||
item.snippet?.thumbnails?.medium?.url ||
item.snippet?.thumbnails?.high?.url ||
'',
}))
return {
success: true,
output: {
items,
totalResults: data.pageInfo?.totalResults || 0,
nextPageToken: data.nextPageToken,
},
}
},
transformError: (error: any): string => {
const message = error.error?.message || error.message || 'YouTube search failed'
const code = error.error?.code || error.code
return `${message} (${code})`
},
}