diff --git a/apps/docs/content/docs/en/tools/parallel_ai.mdx b/apps/docs/content/docs/en/tools/parallel_ai.mdx index 733b9bc2393..55dbfaf10e3 100644 --- a/apps/docs/content/docs/en/tools/parallel_ai.mdx +++ b/apps/docs/content/docs/en/tools/parallel_ai.mdx @@ -44,20 +44,24 @@ Search the web using Parallel AI. Provides comprehensive search results with int | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `objective` | string | Yes | The search objective or question to answer | -| `search_queries` | string | No | Optional comma-separated list of search queries to execute | -| `processor` | string | No | Processing method: base or pro \(default: base\) | -| `max_results` | number | No | Maximum number of results to return \(default: 5\) | -| `max_chars_per_result` | number | No | Maximum characters per result \(default: 1500\) | +| `search_queries` | string | No | Comma-separated list of search queries to execute | +| `mode` | string | No | Search mode: one-shot, agentic, or fast \(default: one-shot\) | +| `max_results` | number | No | Maximum number of results to return \(default: 10\) | +| `max_chars_per_result` | number | No | Maximum characters per result excerpt \(minimum: 1000\) | +| `include_domains` | string | No | Comma-separated list of domains to restrict search results to | +| `exclude_domains` | string | No | Comma-separated list of domains to exclude from search results | | `apiKey` | string | Yes | Parallel AI API Key | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | +| `search_id` | string | Unique identifier for this search request | | `results` | array | Search results with excerpts from relevant pages | | ↳ `url` | string | The URL of the search result | | ↳ `title` | string | The title of the search result | -| ↳ `excerpts` | array | Text excerpts from the page | +| ↳ `publish_date` | string | Publication date of the page \(YYYY-MM-DD\) | +| ↳ `excerpts` | array | LLM-optimized excerpts from the page | ### `parallel_extract` @@ -68,31 +72,33 @@ Extract targeted information from specific URLs using Parallel AI. Processes pro | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `urls` | string | Yes | Comma-separated list of URLs to extract information from | -| `objective` | string | Yes | What information to extract from the provided URLs | -| `excerpts` | boolean | Yes | Include relevant excerpts from the content | -| `full_content` | boolean | Yes | Include full page content | +| `objective` | string | No | What information to extract from the provided URLs | +| `excerpts` | boolean | No | Include relevant excerpts from the content \(default: true\) | +| `full_content` | boolean | No | Include full page content as markdown \(default: false\) | | `apiKey` | string | Yes | Parallel AI API Key | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | +| `extract_id` | string | Unique identifier for this extraction request | | `results` | array | Extracted information from the provided URLs | | ↳ `url` | string | The source URL | | ↳ `title` | string | The title of the page | -| ↳ `content` | string | Extracted content | -| ↳ `excerpts` | array | Relevant text excerpts | +| ↳ `publish_date` | string | Publication date \(YYYY-MM-DD\) | +| ↳ `excerpts` | array | Relevant text excerpts in markdown | +| ↳ `full_content` | string | Full page content as markdown | ### `parallel_deep_research` -Conduct comprehensive deep research across the web using Parallel AI. Synthesizes information from multiple sources with citations. Can take up to 15 minutes to complete. +Conduct comprehensive deep research across the web using Parallel AI. Synthesizes information from multiple sources with citations. Can take up to 45 minutes to complete. #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `input` | string | Yes | Research query or question \(up to 15,000 characters\) | -| `processor` | string | No | Compute level: base, lite, pro, ultra, ultra2x, ultra4x, ultra8x \(default: base\) | +| `processor` | string | No | Processing tier: pro, ultra, pro-fast, ultra-fast \(default: pro\) | | `include_domains` | string | No | Comma-separated list of domains to restrict research to \(source policy\) | | `exclude_domains` | string | No | Comma-separated list of domains to exclude from research \(source policy\) | | `apiKey` | string | Yes | Parallel AI API Key | @@ -101,17 +107,17 @@ Conduct comprehensive deep research across the web using Parallel AI. Synthesize | Parameter | Type | Description | | --------- | ---- | ----------- | -| `status` | string | Task status \(completed, failed\) | +| `status` | string | Task status \(completed, failed, running\) | | `run_id` | string | Unique ID for this research task | | `message` | string | Status message | | `content` | object | Research results \(structured based on output_schema\) | | `basis` | array | Citations and sources with reasoning and confidence levels | -| ↳ `field` | string | Output field name | +| ↳ `field` | string | Output field dot-notation path | | ↳ `reasoning` | string | Explanation for the result | | ↳ `citations` | array | Array of sources | | ↳ `url` | string | Source URL | | ↳ `title` | string | Source title | | ↳ `excerpts` | array | Relevant excerpts from the source | -| ↳ `confidence` | string | Confidence level indicator | +| ↳ `confidence` | string | Confidence level \(high, medium\) | diff --git a/apps/sim/blocks/blocks/parallel.ts b/apps/sim/blocks/blocks/parallel.ts index 96453d37b66..025d0307c93 100644 --- a/apps/sim/blocks/blocks/parallel.ts +++ b/apps/sim/blocks/blocks/parallel.ts @@ -9,7 +9,7 @@ export const ParallelBlock: BlockConfig = { authMode: AuthMode.ApiKey, longDescription: 'Integrate Parallel AI into the workflow. Can search the web, extract information from URLs, and conduct deep research.', - docsLink: 'https://docs.parallel.ai/', + docsLink: 'https://docs.sim.ai/tools/parallel-ai', category: 'tools', bgColor: '#E0E0E0', icon: ParallelIcon, @@ -56,7 +56,7 @@ export const ParallelBlock: BlockConfig = { title: 'Extract Objective', type: 'long-input', placeholder: 'What information to extract from the URLs?', - required: true, + required: false, condition: { field: 'operation', value: 'extract' }, }, { @@ -89,6 +89,37 @@ export const ParallelBlock: BlockConfig = { required: true, condition: { field: 'operation', value: 'deep_research' }, }, + { + id: 'search_mode', + title: 'Search Mode', + type: 'dropdown', + options: [ + { label: 'One-Shot', id: 'one-shot' }, + { label: 'Agentic', id: 'agentic' }, + { label: 'Fast', id: 'fast' }, + ], + value: () => 'one-shot', + condition: { field: 'operation', value: 'search' }, + mode: 'advanced', + }, + { + id: 'search_include_domains', + title: 'Include Domains', + type: 'short-input', + placeholder: 'Comma-separated domains to include (e.g., .edu, example.com)', + required: false, + condition: { field: 'operation', value: 'search' }, + mode: 'advanced', + }, + { + id: 'search_exclude_domains', + title: 'Exclude Domains', + type: 'short-input', + placeholder: 'Comma-separated domains to exclude', + required: false, + condition: { field: 'operation', value: 'search' }, + mode: 'advanced', + }, { id: 'include_domains', title: 'Include Domains', @@ -96,6 +127,7 @@ export const ParallelBlock: BlockConfig = { placeholder: 'Comma-separated domains to include', required: false, condition: { field: 'operation', value: 'deep_research' }, + mode: 'advanced', }, { id: 'exclude_domains', @@ -104,37 +136,37 @@ export const ParallelBlock: BlockConfig = { placeholder: 'Comma-separated domains to exclude', required: false, condition: { field: 'operation', value: 'deep_research' }, + mode: 'advanced', }, { id: 'processor', - title: 'Processor', + title: 'Research Processor', type: 'dropdown', options: [ - { label: 'Lite', id: 'lite' }, - { label: 'Base', id: 'base' }, - { label: 'Core', id: 'core' }, - { label: 'Core 2x', id: 'core2x' }, { label: 'Pro', id: 'pro' }, { label: 'Ultra', id: 'ultra' }, - { label: 'Ultra 2x', id: 'ultra2x' }, - { label: 'Ultra 4x', id: 'ultra4x' }, + { label: 'Pro Fast', id: 'pro-fast' }, + { label: 'Ultra Fast', id: 'ultra-fast' }, ], - value: () => 'base', - condition: { field: 'operation', value: ['search', 'deep_research'] }, + value: () => 'pro', + condition: { field: 'operation', value: 'deep_research' }, + mode: 'advanced', }, { id: 'max_results', title: 'Max Results', type: 'short-input', - placeholder: '5', + placeholder: '10', condition: { field: 'operation', value: 'search' }, + mode: 'advanced', }, { id: 'max_chars_per_result', - title: 'Max Chars', + title: 'Max Chars Per Result', type: 'short-input', placeholder: '1500', condition: { field: 'operation', value: 'search' }, + mode: 'advanced', }, { id: 'apiKey', @@ -149,8 +181,6 @@ export const ParallelBlock: BlockConfig = { access: ['parallel_search', 'parallel_extract', 'parallel_deep_research'], config: { tool: (params) => { - if (params.extract_objective) params.objective = params.extract_objective - if (params.research_input) params.input = params.research_input switch (params.operation) { case 'search': return 'parallel_search' @@ -174,21 +204,30 @@ export const ParallelBlock: BlockConfig = { .filter((query: string) => query.length > 0) if (queries.length > 0) { result.search_queries = queries - } else { - result.search_queries = undefined } } + if (params.search_mode && params.search_mode !== 'one-shot') { + result.mode = params.search_mode + } if (params.max_results) result.max_results = Number(params.max_results) if (params.max_chars_per_result) { result.max_chars_per_result = Number(params.max_chars_per_result) } + result.include_domains = params.search_include_domains || undefined + result.exclude_domains = params.search_exclude_domains || undefined } if (operation === 'extract') { + if (params.extract_objective) result.objective = params.extract_objective result.excerpts = !(params.excerpts === 'false' || params.excerpts === false) result.full_content = params.full_content === 'true' || params.full_content === true } + if (operation === 'deep_research') { + if (params.research_input) result.input = params.research_input + if (params.processor) result.processor = params.processor + } + return result }, }, @@ -202,29 +241,34 @@ export const ParallelBlock: BlockConfig = { excerpts: { type: 'boolean', description: 'Include excerpts' }, full_content: { type: 'boolean', description: 'Include full content' }, research_input: { type: 'string', description: 'Deep research query' }, - include_domains: { type: 'string', description: 'Domains to include' }, - exclude_domains: { type: 'string', description: 'Domains to exclude' }, - processor: { type: 'string', description: 'Processing method' }, + include_domains: { type: 'string', description: 'Domains to include (deep research)' }, + exclude_domains: { type: 'string', description: 'Domains to exclude (deep research)' }, + search_include_domains: { type: 'string', description: 'Domains to include (search)' }, + search_exclude_domains: { type: 'string', description: 'Domains to exclude (search)' }, + search_mode: { type: 'string', description: 'Search mode (one-shot, agentic, fast)' }, + processor: { type: 'string', description: 'Research processing tier' }, max_results: { type: 'number', description: 'Maximum number of results' }, max_chars_per_result: { type: 'number', description: 'Maximum characters per result' }, apiKey: { type: 'string', description: 'Parallel AI API key' }, }, outputs: { - results: { type: 'string', description: 'Search or extract results (JSON stringified)' }, + results: { + type: 'json', + description: 'Search or extract results (array of url, title, excerpts)', + }, + search_id: { type: 'string', description: 'Search request ID (for search)' }, + extract_id: { type: 'string', description: 'Extract request ID (for extract)' }, status: { type: 'string', description: 'Task status (for deep research)' }, run_id: { type: 'string', description: 'Task run ID (for deep research)' }, message: { type: 'string', description: 'Status message (for deep research)' }, content: { - type: 'string', - description: 'Research content (for deep research, JSON stringified)', + type: 'json', + description: 'Research content (for deep research, structured based on output_schema)', }, basis: { - type: 'string', - description: 'Citations and sources (for deep research, JSON stringified)', - }, - metadata: { - type: 'string', - description: 'Task metadata (for deep research, JSON stringified)', + type: 'json', + description: + 'Citations and sources with field, reasoning, citations, confidence (for deep research)', }, }, } diff --git a/apps/sim/tools/parallel/deep_research.ts b/apps/sim/tools/parallel/deep_research.ts index 1533af232e9..8a2fdb6112d 100644 --- a/apps/sim/tools/parallel/deep_research.ts +++ b/apps/sim/tools/parallel/deep_research.ts @@ -8,7 +8,7 @@ export const deepResearchTool: ToolConfig { const body: Record = { input: params.input, - processor: params.processor || 'base', + processor: params.processor || 'pro', + task_spec: { + output_schema: 'auto', + }, } - const taskSpec: Record = {} - - taskSpec.output_schema = 'auto' - - body.task_spec = taskSpec - if (params.include_domains || params.exclude_domains) { const sourcePolicy: Record = {} @@ -91,14 +87,21 @@ export const deepResearchTool: ToolConfig { + if (!response.ok) { + const errorText = await response.text() + throw new Error( + `Parallel AI deep research task creation failed: ${response.status} - ${errorText}` + ) + } + const data = await response.json() return { success: true, output: { - run_id: data.run_id, - status: data.status, - message: `Research task ${data.status}, waiting for completion...`, + run_id: data.run_id ?? null, + status: data.status ?? null, + message: `Research task ${data.status ?? 'created'}, waiting for completion...`, content: {}, basis: [], }, @@ -122,13 +125,16 @@ export const deepResearchTool: ToolConfig = { }, objective: { type: 'string', - required: true, + required: false, visibility: 'user-or-llm', description: 'What information to extract from the provided URLs', }, excerpts: { type: 'boolean', - required: true, - visibility: 'user-only', - description: 'Include relevant excerpts from the content', + required: false, + visibility: 'user-or-llm', + description: 'Include relevant excerpts from the content (default: true)', }, full_content: { type: 'boolean', - required: true, - visibility: 'user-only', - description: 'Include full page content', + required: false, + visibility: 'user-or-llm', + description: 'Include full page content as markdown (default: false)', }, apiKey: { type: 'string', @@ -50,7 +50,6 @@ export const extractTool: ToolConfig = { 'parallel-beta': 'search-extract-2025-10-10', }), body: (params) => { - // Convert comma-separated URLs to array const urlArray = params.urls .split(',') .map((url) => url.trim()) @@ -58,10 +57,9 @@ export const extractTool: ToolConfig = { const body: Record = { urls: urlArray, - objective: params.objective, } - // Add optional parameters if provided + if (params.objective) body.objective = params.objective if (params.excerpts !== undefined) body.excerpts = params.excerpts if (params.full_content !== undefined) body.full_content = params.full_content @@ -70,17 +68,44 @@ export const extractTool: ToolConfig = { }, transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Parallel AI extract failed: ${response.status} - ${errorText}`) + } + const data = await response.json() + if (!data.results) { + return { + success: false, + error: 'No results returned from extraction', + output: { + results: [], + extract_id: data.extract_id ?? null, + }, + } + } + return { success: true, output: { - results: data.results || [], + extract_id: data.extract_id ?? null, + results: data.results.map((result: Record) => ({ + url: result.url ?? null, + title: result.title ?? null, + publish_date: result.publish_date ?? null, + excerpts: result.excerpts ?? [], + full_content: result.full_content ?? null, + })), }, } }, outputs: { + extract_id: { + type: 'string', + description: 'Unique identifier for this extraction request', + }, results: { type: 'array', description: 'Extracted information from the provided URLs', @@ -88,12 +113,22 @@ export const extractTool: ToolConfig = { type: 'object', properties: { url: { type: 'string', description: 'The source URL' }, - title: { type: 'string', description: 'The title of the page' }, - content: { type: 'string', description: 'Extracted content' }, + title: { type: 'string', description: 'The title of the page', optional: true }, + publish_date: { + type: 'string', + description: 'Publication date (YYYY-MM-DD)', + optional: true, + }, excerpts: { type: 'array', - description: 'Relevant text excerpts', + description: 'Relevant text excerpts in markdown', items: { type: 'string' }, + optional: true, + }, + full_content: { + type: 'string', + description: 'Full page content as markdown', + optional: true, }, }, }, diff --git a/apps/sim/tools/parallel/index.ts b/apps/sim/tools/parallel/index.ts index 7e9c9abb748..585123cf6c2 100644 --- a/apps/sim/tools/parallel/index.ts +++ b/apps/sim/tools/parallel/index.ts @@ -5,3 +5,5 @@ import { searchTool } from '@/tools/parallel/search' export const parallelSearchTool = searchTool export const parallelExtractTool = extractTool export const parallelDeepResearchTool = deepResearchTool + +export * from './types' diff --git a/apps/sim/tools/parallel/search.ts b/apps/sim/tools/parallel/search.ts index 6cd919a1404..ef737694461 100644 --- a/apps/sim/tools/parallel/search.ts +++ b/apps/sim/tools/parallel/search.ts @@ -19,25 +19,37 @@ export const searchTool: ToolConfig = { type: 'string', required: false, visibility: 'user-or-llm', - description: 'Optional comma-separated list of search queries to execute', + description: 'Comma-separated list of search queries to execute', }, - processor: { + mode: { type: 'string', required: false, visibility: 'user-only', - description: 'Processing method: base or pro (default: base)', + description: 'Search mode: one-shot, agentic, or fast (default: one-shot)', }, max_results: { type: 'number', required: false, visibility: 'user-only', - description: 'Maximum number of results to return (default: 5)', + description: 'Maximum number of results to return (default: 10)', }, max_chars_per_result: { type: 'number', required: false, visibility: 'user-only', - description: 'Maximum characters per result (default: 1500)', + description: 'Maximum characters per result excerpt (minimum: 1000)', + }, + include_domains: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated list of domains to restrict search results to', + }, + exclude_domains: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated list of domains to exclude from search results', }, apiKey: { type: 'string', @@ -60,44 +72,83 @@ export const searchTool: ToolConfig = { objective: params.objective, } - // Only include search_queries if it's not empty - if ( - params.search_queries !== undefined && - params.search_queries !== null && - params.search_queries.length > 0 - ) { - body.search_queries = params.search_queries + if (params.search_queries) { + if (Array.isArray(params.search_queries)) { + body.search_queries = params.search_queries + } else if (typeof params.search_queries === 'string') { + const queries = params.search_queries + .split(',') + .map((q: string) => q.trim()) + .filter((q: string) => q.length > 0) + if (queries.length > 0) body.search_queries = queries + } } - // Add optional parameters if provided - if (params.processor) body.processor = params.processor + if (params.mode) body.mode = params.mode if (params.max_results) body.max_results = Number(params.max_results) - if (params.max_chars_per_result) - body.max_chars_per_result = Number(params.max_chars_per_result) + if (params.max_chars_per_result) { + body.excerpts = { max_chars_per_result: Number(params.max_chars_per_result) } + } + + const sourcePolicy: Record = {} + if (params.include_domains) { + sourcePolicy.include_domains = params.include_domains + .split(',') + .map((d: string) => d.trim()) + .filter((d: string) => d.length > 0) + } + if (params.exclude_domains) { + sourcePolicy.exclude_domains = params.exclude_domains + .split(',') + .map((d: string) => d.trim()) + .filter((d: string) => d.length > 0) + } + if (Object.keys(sourcePolicy).length > 0) { + body.source_policy = sourcePolicy + } return body }, }, transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Parallel AI search failed: ${response.status} - ${errorText}`) + } + const data = await response.json() + if (!data.results) { + return { + success: false, + error: 'No results returned from search', + output: { + results: [], + search_id: data.search_id ?? null, + }, + } + } + return { success: true, output: { - results: data.results.map((result: unknown) => { - const resultObj = result as Record - return { - url: resultObj.url || '', - title: resultObj.title || '', - excerpts: resultObj.excerpts || [], - } - }), + search_id: data.search_id ?? null, + results: data.results.map((result: Record) => ({ + url: result.url ?? null, + title: result.title ?? null, + publish_date: result.publish_date ?? null, + excerpts: result.excerpts ?? [], + })), }, } }, outputs: { + search_id: { + type: 'string', + description: 'Unique identifier for this search request', + }, results: { type: 'array', description: 'Search results with excerpts from relevant pages', @@ -106,9 +157,14 @@ export const searchTool: ToolConfig = { properties: { url: { type: 'string', description: 'The URL of the search result' }, title: { type: 'string', description: 'The title of the search result' }, + publish_date: { + type: 'string', + description: 'Publication date of the page (YYYY-MM-DD)', + optional: true, + }, excerpts: { type: 'array', - description: 'Text excerpts from the page', + description: 'LLM-optimized excerpts from the page', items: { type: 'string' }, }, }, diff --git a/apps/sim/tools/parallel/types.ts b/apps/sim/tools/parallel/types.ts index bca8fd437f0..e1f1707f215 100644 --- a/apps/sim/tools/parallel/types.ts +++ b/apps/sim/tools/parallel/types.ts @@ -1,39 +1,51 @@ +import type { ToolResponse } from '@/tools/types' + export interface ParallelSearchParams { objective: string - search_queries: string[] - processor?: string + search_queries?: string[] | string + mode?: string max_results?: number max_chars_per_result?: number + include_domains?: string + exclude_domains?: string apiKey: string } export interface ParallelSearchResult { - url: string - title: string + url: string | null + title: string | null + publish_date?: string | null excerpts: string[] } -export interface ParallelSearchResponse { - results: ParallelSearchResult[] +export interface ParallelSearchResponse extends ToolResponse { + output: { + search_id: string | null + results: ParallelSearchResult[] + } } export interface ParallelExtractParams { urls: string - objective: string - excerpts: boolean - full_content: boolean + objective?: string + excerpts?: boolean + full_content?: boolean apiKey: string } export interface ParallelExtractResult { - url: string - title: string - content?: string + url: string | null + title?: string | null + publish_date?: string | null excerpts?: string[] + full_content?: string | null } -export interface ParallelExtractResponse { - results: ParallelExtractResult[] +export interface ParallelExtractResponse extends ToolResponse { + output: { + extract_id: string | null + results: ParallelExtractResult[] + } } export interface ParallelDeepResearchParams { @@ -45,17 +57,22 @@ export interface ParallelDeepResearchParams { } export interface ParallelDeepResearchBasis { - url: string - title: string - excerpt: string - confidence?: number -} - -export interface ParallelDeepResearchResponse { - status: string - run_id: string - message?: string - content?: Record - basis?: ParallelDeepResearchBasis[] - metadata?: Record + field: string + reasoning: string + citations: { + url: string + title: string + excerpts: string[] + }[] + confidence: string +} + +export interface ParallelDeepResearchResponse extends ToolResponse { + output: { + status: string + run_id: string + message: string + content: Record + basis: ParallelDeepResearchBasis[] + } }