diff --git a/blocks/blocks/crewai.ts b/blocks/blocks/crewai.ts index b116a3eaec..8080d89f5f 100644 --- a/blocks/blocks/crewai.ts +++ b/blocks/blocks/crewai.ts @@ -3,7 +3,7 @@ import { CrewAIIcon } from '@/components/icons' import { VisionResponse } from '@/tools/crewai/vision' export const CrewAIVisionBlock: BlockConfig = { - type: 'crewaivision', + type: 'crewai_vision', toolbar: { title: 'CrewAI Vision Tool', description: 'Analyze images using vision models', diff --git a/blocks/blocks/firecrawl.ts b/blocks/blocks/firecrawl.ts index 95fcbdca1a..2bc090d57f 100644 --- a/blocks/blocks/firecrawl.ts +++ b/blocks/blocks/firecrawl.ts @@ -3,7 +3,7 @@ import { FirecrawlIcon } from '@/components/icons' import { ScrapeResponse } from '@/tools/firecrawl/scrape' export const FirecrawlScrapeBlock: BlockConfig = { - type: 'firecrawlscrape', + type: 'firecrawl_scrape', toolbar: { title: 'Firecrawl Scraper', description: 'Extract clean content from any webpage', diff --git a/blocks/blocks/function.ts b/blocks/blocks/function.ts index 216b64e392..b1a68403d3 100644 --- a/blocks/blocks/function.ts +++ b/blocks/blocks/function.ts @@ -32,6 +32,7 @@ export const FunctionBlock: BlockConfig = { title: 'Code', type: 'code', layout: 'full', + placeholder: 'Enter your code here...' } ], }, diff --git a/blocks/blocks/jina.ts b/blocks/blocks/jina.ts new file mode 100644 index 0000000000..723d974118 --- /dev/null +++ b/blocks/blocks/jina.ts @@ -0,0 +1,74 @@ +import { JinaAIIcon } from '@/components/icons' +import { BlockConfig } from '../types' +import { ReadUrlResponse } from '@/tools/jina/reader' + +export const JinaBlock: BlockConfig = { + type: 'jina_reader', + toolbar: { + title: 'Jina Reader', + description: 'Convert any URL to LLM-friendly text', + bgColor: '#ffffff', + icon: JinaAIIcon, + category: 'advanced', + }, + tools: { + access: ['jina.readurl'] + }, + workflow: { + inputs: { + url: { type: 'string', required: true }, + useReaderLMv2: { type: 'boolean', required: false }, + removeImages: { type: 'boolean', required: false }, + gatherLinks: { type: 'boolean', required: false }, + jsonResponse: { type: 'boolean', required: false } + }, + outputs: { + response: { + type: { + content: 'string' + } + } + }, + subBlocks: [ + { + id: 'url', + title: 'URL', + type: 'short-input', + layout: 'full', + placeholder: 'Enter URL to read', + }, + { + id: 'useReaderLMv2', + title: 'Use ReaderLM v2', + type: 'switch', + layout: 'half', + }, + { + id: 'removeImages', + title: 'Remove Images', + type: 'switch', + layout: 'half', + }, + { + id: 'gatherLinks', + title: 'Gather Links', + type: 'switch', + layout: 'half', + }, + { + id: 'jsonResponse', + title: 'JSON Response', + type: 'switch', + layout: 'half', + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Enter API Key (optional)', + password: true + } + ], + }, +} diff --git a/blocks/index.ts b/blocks/index.ts index 2c3de81942..d75f00ad57 100644 --- a/blocks/index.ts +++ b/blocks/index.ts @@ -6,17 +6,26 @@ import { ApiBlock } from './blocks/api' import { FunctionBlock } from './blocks/function' import { CrewAIVisionBlock } from './blocks/crewai' import { FirecrawlScrapeBlock } from './blocks/firecrawl' +import { JinaBlock } from './blocks/jina' // Export blocks for ease of use -export { AgentBlock, ApiBlock, FunctionBlock, CrewAIVisionBlock, FirecrawlScrapeBlock } +export { + AgentBlock, + ApiBlock, + FunctionBlock, + CrewAIVisionBlock, + FirecrawlScrapeBlock, + JinaBlock +} // Registry of all block configurations const blocks: Record = { agent: AgentBlock, api: ApiBlock, function: FunctionBlock, - crewaivision: CrewAIVisionBlock, - firecrawlscrape: FirecrawlScrapeBlock + crewai_vision: CrewAIVisionBlock, + firecrawl_scrape: FirecrawlScrapeBlock, + jina_reader: JinaBlock } // Build a reverse mapping of tools to block types diff --git a/components/icons.tsx b/components/icons.tsx index 5ef496814e..a10d256c92 100644 --- a/components/icons.tsx +++ b/components/icons.tsx @@ -910,3 +910,32 @@ export const FirecrawlIcon = (props: SVGProps) => ( ) + +export function JinaAIIcon(props: SVGProps) { + return ( + + + + + + + + ) +} diff --git a/tools/index.ts b/tools/index.ts index af4eb8e1bf..6aa254eb54 100644 --- a/tools/index.ts +++ b/tools/index.ts @@ -11,6 +11,7 @@ import { opportunitiesTool as salesforceOpportunities } from './salesforce/oppor import { functionExecuteTool as functionExecute } from './function/execute' import { visionTool as crewAIVision } from './crewai/vision' import { scrapeTool } from './firecrawl/scrape' +import { readUrlTool } from './jina/reader' // Registry of all available tools export const tools: Record = { // AI Models @@ -30,7 +31,9 @@ export const tools: Record = { // CrewAI Tools 'crewai.vision': crewAIVision, // Firecrawl Tools - 'firecrawl.scrape': scrapeTool + 'firecrawl.scrape': scrapeTool, + // Jina Tools + 'jina.readurl': readUrlTool } // Get a tool by its ID diff --git a/tools/jina/reader.ts b/tools/jina/reader.ts new file mode 100644 index 0000000000..fdc4d644b9 --- /dev/null +++ b/tools/jina/reader.ts @@ -0,0 +1,84 @@ +import { ToolConfig, ToolResponse } from '../types' + +export interface ReadUrlParams { + url: string + useReaderLMv2?: boolean + removeImages?: boolean + gatherLinks?: boolean + jsonResponse?: boolean + apiKey?: string +} + +export interface ReadUrlResponse extends ToolResponse { + output: { + content: string + } +} + +export const readUrlTool: ToolConfig = { + id: 'jina.readurl', + name: 'Jina Reader', + description: 'Convert any URL to LLM-friendly text using Jina AI Reader', + version: '1.0.0', + + params: { + url: { + type: 'string', + required: true, + description: 'The URL to read and convert to markdown' + }, + useReaderLMv2: { + type: 'boolean', + description: 'Whether to use ReaderLM-v2 for better quality' + }, + removeImages: { + type: 'boolean', + description: 'Whether to remove all images from the response' + }, + gatherLinks: { + type: 'boolean', + description: 'Whether to gather all links at the end' + }, + jsonResponse: { + type: 'boolean', + description: 'Whether to return response in JSON format' + }, + apiKey: { + type: 'string', + description: 'Your Jina AI API key' + } + }, + + request: { + url: (params: ReadUrlParams) => { + const baseUrl = `https://r.jina.ai/https://${params.url.replace(/^https?:\/\//, '')}` + const queryParams = new URLSearchParams() + + if (params.useReaderLMv2) queryParams.append('use_readerlm_v2', 'true') + if (params.removeImages) queryParams.append('remove_images', 'true') + if (params.gatherLinks) queryParams.append('gather_links', 'true') + if (params.jsonResponse) queryParams.append('json_response', 'true') + + return `${baseUrl}${queryParams.toString() ? '?' + queryParams.toString() : ''}` + }, + method: 'GET', + headers: (params: ReadUrlParams) => ({ + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${params.apiKey}` + }) + }, + + transformResponse: async (response: Response) => { + const content = await response.text() + return { + success: response.ok, + output: { + content + } + } + }, + + transformError: (error) => { + return error instanceof Error ? error.message : 'Failed to read URL' + } +}