mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
improvement(oauth): added advanced mode for all tools with oauth and selectors (#721)
* fixed SVG error, added tool-level awareness for param validation * added advanced mode for oauth block * added wealthbox advanced mode * fixed wealthbox * moved types to types file * ack pr comments
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { AgentBlock } from './agent'
|
||||
import { AgentBlock } from '@/blocks/blocks/agent'
|
||||
|
||||
vi.mock('@/blocks', () => ({
|
||||
getAllBlocks: vi.fn(() => [
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { AgentIcon } from '@/components/icons'
|
||||
import { isHosted } from '@/lib/environment'
|
||||
import { createLogger } from '@/lib/logs/console-logger'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import {
|
||||
getAllModelProviders,
|
||||
getBaseModelProviders,
|
||||
@@ -13,7 +14,6 @@ import {
|
||||
} from '@/providers/utils'
|
||||
import { useOllamaStore } from '@/stores/ollama/store'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
const logger = createLogger('AgentBlock')
|
||||
|
||||
|
||||
@@ -1,19 +1,6 @@
|
||||
import { AirtableIcon } from '@/components/icons'
|
||||
import type {
|
||||
AirtableCreateResponse,
|
||||
AirtableGetResponse,
|
||||
AirtableListResponse,
|
||||
AirtableUpdateMultipleResponse,
|
||||
AirtableUpdateResponse,
|
||||
} from '@/tools/airtable/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type AirtableResponse =
|
||||
| AirtableListResponse
|
||||
| AirtableGetResponse
|
||||
| AirtableCreateResponse
|
||||
| AirtableUpdateResponse
|
||||
| AirtableUpdateMultipleResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { AirtableResponse } from '@/tools/airtable/types'
|
||||
|
||||
export const AirtableBlock: BlockConfig<AirtableResponse> = {
|
||||
type: 'airtable',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ApiIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { RequestResponse } from '@/tools/http/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const ApiBlock: BlockConfig<RequestResponse> = {
|
||||
type: 'api',
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import { BrowserUseIcon } from '@/components/icons'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
interface BrowserUseResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
success: boolean
|
||||
output: any
|
||||
steps: any[]
|
||||
}
|
||||
}
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { BrowserUseResponse } from '@/tools/browser_use/types'
|
||||
|
||||
export const BrowserUseBlock: BlockConfig<BrowserUseResponse> = {
|
||||
type: 'browser_use',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ClayIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { ClayPopulateResponse } from '@/tools/clay/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const ClayBlock: BlockConfig<ClayPopulateResponse> = {
|
||||
type: 'clay',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ConditionalIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '../types'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
|
||||
interface ConditionBlockOutput {
|
||||
success: boolean
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { ConfluenceIcon } from '@/components/icons'
|
||||
import type { ConfluenceRetrieveResponse, ConfluenceUpdateResponse } from '@/tools/confluence/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type ConfluenceResponse = ConfluenceRetrieveResponse | ConfluenceUpdateResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { ConfluenceResponse } from '@/tools/confluence/types'
|
||||
|
||||
export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
type: 'confluence',
|
||||
@@ -48,7 +46,7 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
],
|
||||
placeholder: 'Select Confluence account',
|
||||
},
|
||||
// Use file-selector component for page selection
|
||||
// Page selector (basic mode)
|
||||
{
|
||||
id: 'pageId',
|
||||
title: 'Select Page',
|
||||
@@ -57,6 +55,16 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
provider: 'confluence',
|
||||
serviceId: 'confluence',
|
||||
placeholder: 'Select Confluence page',
|
||||
mode: 'basic',
|
||||
},
|
||||
// Manual page ID input (advanced mode)
|
||||
{
|
||||
id: 'manualPageId',
|
||||
title: 'Page ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Confluence page ID',
|
||||
mode: 'advanced',
|
||||
},
|
||||
// Update page fields
|
||||
{
|
||||
@@ -90,10 +98,18 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { credential, ...rest } = params
|
||||
const { credential, pageId, manualPageId, ...rest } = params
|
||||
|
||||
// Use the selected page ID or the manually entered one
|
||||
const effectivePageId = (pageId || manualPageId || '').trim()
|
||||
|
||||
if (!effectivePageId) {
|
||||
throw new Error('Page ID is required. Please select a page or enter a page ID manually.')
|
||||
}
|
||||
|
||||
return {
|
||||
accessToken: credential,
|
||||
pageId: effectivePageId,
|
||||
...rest,
|
||||
}
|
||||
},
|
||||
@@ -103,7 +119,8 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
operation: { type: 'string', required: true },
|
||||
domain: { type: 'string', required: true },
|
||||
credential: { type: 'string', required: true },
|
||||
pageId: { type: 'string', required: true },
|
||||
pageId: { type: 'string', required: false },
|
||||
manualPageId: { type: 'string', required: false },
|
||||
// Update operation inputs
|
||||
title: { type: 'string', required: false },
|
||||
content: { type: 'string', required: false },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DiscordIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { DiscordResponse } from '@/tools/discord/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
type: 'discord',
|
||||
@@ -32,6 +32,7 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
placeholder: 'Enter Discord bot token',
|
||||
password: true,
|
||||
},
|
||||
// Server selector (basic mode)
|
||||
{
|
||||
id: 'serverId',
|
||||
title: 'Server',
|
||||
@@ -40,11 +41,26 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
provider: 'discord',
|
||||
serviceId: 'discord',
|
||||
placeholder: 'Select Discord server',
|
||||
mode: 'basic',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['discord_send_message', 'discord_get_messages', 'discord_get_server'],
|
||||
},
|
||||
},
|
||||
// Manual server ID input (advanced mode)
|
||||
{
|
||||
id: 'manualServerId',
|
||||
title: 'Server ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Discord server ID',
|
||||
mode: 'advanced',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['discord_send_message', 'discord_get_messages', 'discord_get_server'],
|
||||
},
|
||||
},
|
||||
// Channel selector (basic mode)
|
||||
{
|
||||
id: 'channelId',
|
||||
title: 'Channel',
|
||||
@@ -53,6 +69,17 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
provider: 'discord',
|
||||
serviceId: 'discord',
|
||||
placeholder: 'Select Discord channel',
|
||||
mode: 'basic',
|
||||
condition: { field: 'operation', value: ['discord_send_message', 'discord_get_messages'] },
|
||||
},
|
||||
// Manual channel ID input (advanced mode)
|
||||
{
|
||||
id: 'manualChannelId',
|
||||
title: 'Channel ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Discord channel ID',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: ['discord_send_message', 'discord_get_messages'] },
|
||||
},
|
||||
{
|
||||
@@ -108,25 +135,56 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
if (!params.botToken) throw new Error('Bot token required for this operation')
|
||||
commonParams.botToken = params.botToken
|
||||
|
||||
// Handle server ID (selector or manual)
|
||||
const effectiveServerId = (params.serverId || params.manualServerId || '').trim()
|
||||
|
||||
// Handle channel ID (selector or manual)
|
||||
const effectiveChannelId = (params.channelId || params.manualChannelId || '').trim()
|
||||
|
||||
switch (params.operation) {
|
||||
case 'discord_send_message':
|
||||
if (!effectiveServerId) {
|
||||
throw new Error(
|
||||
'Server ID is required. Please select a server or enter a server ID manually.'
|
||||
)
|
||||
}
|
||||
if (!effectiveChannelId) {
|
||||
throw new Error(
|
||||
'Channel ID is required. Please select a channel or enter a channel ID manually.'
|
||||
)
|
||||
}
|
||||
return {
|
||||
...commonParams,
|
||||
serverId: params.serverId,
|
||||
channelId: params.channelId,
|
||||
serverId: effectiveServerId,
|
||||
channelId: effectiveChannelId,
|
||||
content: params.content,
|
||||
}
|
||||
case 'discord_get_messages':
|
||||
if (!effectiveServerId) {
|
||||
throw new Error(
|
||||
'Server ID is required. Please select a server or enter a server ID manually.'
|
||||
)
|
||||
}
|
||||
if (!effectiveChannelId) {
|
||||
throw new Error(
|
||||
'Channel ID is required. Please select a channel or enter a channel ID manually.'
|
||||
)
|
||||
}
|
||||
return {
|
||||
...commonParams,
|
||||
serverId: params.serverId,
|
||||
channelId: params.channelId,
|
||||
serverId: effectiveServerId,
|
||||
channelId: effectiveChannelId,
|
||||
limit: params.limit ? Math.min(Math.max(1, Number(params.limit)), 100) : 10,
|
||||
}
|
||||
case 'discord_get_server':
|
||||
if (!effectiveServerId) {
|
||||
throw new Error(
|
||||
'Server ID is required. Please select a server or enter a server ID manually.'
|
||||
)
|
||||
}
|
||||
return {
|
||||
...commonParams,
|
||||
serverId: params.serverId,
|
||||
serverId: effectiveServerId,
|
||||
}
|
||||
case 'discord_get_user':
|
||||
return {
|
||||
@@ -143,7 +201,9 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
operation: { type: 'string', required: true },
|
||||
botToken: { type: 'string', required: true },
|
||||
serverId: { type: 'string', required: false },
|
||||
manualServerId: { type: 'string', required: false },
|
||||
channelId: { type: 'string', required: false },
|
||||
manualChannelId: { type: 'string', required: false },
|
||||
content: { type: 'string', required: false },
|
||||
limit: { type: 'number', required: false },
|
||||
userId: { type: 'string', required: false },
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import { ElevenLabsIcon } from '@/components/icons'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
interface ElevenLabsBlockResponse extends ToolResponse {
|
||||
output: {
|
||||
audioUrl: string
|
||||
}
|
||||
}
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { ElevenLabsBlockResponse } from '@/tools/elevenlabs/types'
|
||||
|
||||
export const ElevenLabsBlock: BlockConfig<ElevenLabsBlockResponse> = {
|
||||
type: 'elevenlabs',
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ChartBarIcon } from '@/components/icons'
|
||||
import { isHosted } from '@/lib/environment'
|
||||
import { createLogger } from '@/lib/logs/console-logger'
|
||||
import type { BlockConfig, ParamType } from '@/blocks/types'
|
||||
import type { ProviderId } from '@/providers/types'
|
||||
import { getAllModelProviders, getBaseModelProviders, getHostedModels } from '@/providers/utils'
|
||||
import { useOllamaStore } from '@/stores/ollama/store'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
import type { BlockConfig, ParamType } from '../types'
|
||||
|
||||
const logger = createLogger('EvaluatorBlock')
|
||||
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
import { ExaAIIcon } from '@/components/icons'
|
||||
import type {
|
||||
ExaAnswerResponse,
|
||||
ExaFindSimilarLinksResponse,
|
||||
ExaGetContentsResponse,
|
||||
ExaSearchResponse,
|
||||
} from '@/tools/exa/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type ExaResponse =
|
||||
| ExaSearchResponse
|
||||
| ExaGetContentsResponse
|
||||
| ExaFindSimilarLinksResponse
|
||||
| ExaAnswerResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { ExaResponse } from '@/tools/exa/types'
|
||||
|
||||
export const ExaBlock: BlockConfig<ExaResponse> = {
|
||||
type: 'exa',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { DocumentIcon } from '@/components/icons'
|
||||
import { isProd } from '@/lib/environment'
|
||||
import { createLogger } from '@/lib/logs/console-logger'
|
||||
import type { BlockConfig, SubBlockConfig, SubBlockLayout, SubBlockType } from '@/blocks/types'
|
||||
import type { FileParserOutput } from '@/tools/file/types'
|
||||
import type { BlockConfig, SubBlockConfig, SubBlockLayout, SubBlockType } from '../types'
|
||||
|
||||
const logger = createLogger('FileBlock')
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { FirecrawlIcon } from '@/components/icons'
|
||||
import type { ScrapeResponse, SearchResponse } from '@/tools/firecrawl/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type FirecrawlResponse = ScrapeResponse | SearchResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { FirecrawlResponse } from '@/tools/firecrawl/types'
|
||||
|
||||
export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
|
||||
type: 'firecrawl',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CodeIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { CodeExecutionOutput } from '@/tools/function/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const FunctionBlock: BlockConfig<CodeExecutionOutput> = {
|
||||
type: 'function',
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
import { GithubIcon } from '@/components/icons'
|
||||
import type {
|
||||
CreateCommentResponse,
|
||||
LatestCommitResponse,
|
||||
PullRequestResponse,
|
||||
RepoInfoResponse,
|
||||
} from '@/tools/github/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type GitHubResponse =
|
||||
| PullRequestResponse
|
||||
| CreateCommentResponse
|
||||
| LatestCommitResponse
|
||||
| RepoInfoResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { GitHubResponse } from '@/tools/github/types'
|
||||
|
||||
export const GitHubBlock: BlockConfig<GitHubResponse> = {
|
||||
type: 'github',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { GmailIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { GmailToolResponse } from '@/tools/gmail/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
type: 'gmail',
|
||||
@@ -67,7 +67,7 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
placeholder: 'Email content',
|
||||
condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] },
|
||||
},
|
||||
// Read Email Fields - Add folder selector
|
||||
// Label/folder selector (basic mode)
|
||||
{
|
||||
id: 'folder',
|
||||
title: 'Label',
|
||||
@@ -80,6 +80,17 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
'https://www.googleapis.com/auth/gmail.labels',
|
||||
],
|
||||
placeholder: 'Select Gmail label/folder',
|
||||
mode: 'basic',
|
||||
condition: { field: 'operation', value: 'read_gmail' },
|
||||
},
|
||||
// Manual label/folder input (advanced mode)
|
||||
{
|
||||
id: 'manualFolder',
|
||||
title: 'Label/Folder',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Gmail label name (e.g., INBOX, SENT, or custom label)',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: 'read_gmail' },
|
||||
},
|
||||
{
|
||||
@@ -141,11 +152,14 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
},
|
||||
params: (params) => {
|
||||
// Pass the credential directly from the credential field
|
||||
const { credential, ...rest } = params
|
||||
const { credential, folder, manualFolder, ...rest } = params
|
||||
|
||||
// Handle folder input (selector or manual)
|
||||
const effectiveFolder = (folder || manualFolder || '').trim()
|
||||
|
||||
// Ensure folder is always provided for read_gmail operation
|
||||
if (rest.operation === 'read_gmail') {
|
||||
rest.folder = rest.folder || 'INBOX'
|
||||
rest.folder = effectiveFolder || 'INBOX'
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -164,6 +178,7 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
body: { type: 'string', required: false },
|
||||
// Read operation inputs
|
||||
folder: { type: 'string', required: false },
|
||||
manualFolder: { type: 'string', required: false },
|
||||
messageId: { type: 'string', required: false },
|
||||
unreadOnly: { type: 'boolean', required: false },
|
||||
// Search operation inputs
|
||||
|
||||
@@ -1,24 +1,6 @@
|
||||
import { GoogleIcon } from '@/components/icons'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
interface GoogleSearchResponse extends ToolResponse {
|
||||
output: {
|
||||
items: Array<{
|
||||
title: string
|
||||
link: string
|
||||
snippet: string
|
||||
displayLink?: string
|
||||
pagemap?: Record<string, any>
|
||||
}>
|
||||
searchInformation: {
|
||||
totalResults: string
|
||||
searchTime: number
|
||||
formattedSearchTime: string
|
||||
formattedTotalResults: string
|
||||
}
|
||||
}
|
||||
}
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { GoogleSearchResponse } from '@/tools/google/types'
|
||||
|
||||
export const GoogleSearchBlock: BlockConfig<GoogleSearchResponse> = {
|
||||
type: 'google_search',
|
||||
|
||||
@@ -1,19 +1,6 @@
|
||||
import { GoogleCalendarIcon } from '@/components/icons'
|
||||
import type {
|
||||
GoogleCalendarCreateResponse,
|
||||
GoogleCalendarGetResponse,
|
||||
GoogleCalendarInviteResponse,
|
||||
GoogleCalendarListResponse,
|
||||
GoogleCalendarQuickAddResponse,
|
||||
} from '@/tools/google_calendar/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type GoogleCalendarResponse =
|
||||
| GoogleCalendarCreateResponse
|
||||
| GoogleCalendarListResponse
|
||||
| GoogleCalendarGetResponse
|
||||
| GoogleCalendarQuickAddResponse
|
||||
| GoogleCalendarInviteResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { GoogleCalendarResponse } from '@/tools/google_calendar/types'
|
||||
|
||||
export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
|
||||
type: 'google_calendar',
|
||||
@@ -49,6 +36,7 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
|
||||
requiredScopes: ['https://www.googleapis.com/auth/calendar'],
|
||||
placeholder: 'Select Google Calendar account',
|
||||
},
|
||||
// Calendar selector (basic mode)
|
||||
{
|
||||
id: 'calendarId',
|
||||
title: 'Calendar',
|
||||
@@ -58,6 +46,16 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
|
||||
serviceId: 'google-calendar',
|
||||
requiredScopes: ['https://www.googleapis.com/auth/calendar'],
|
||||
placeholder: 'Select calendar',
|
||||
mode: 'basic',
|
||||
},
|
||||
// Manual calendar ID input (advanced mode)
|
||||
{
|
||||
id: 'manualCalendarId',
|
||||
title: 'Calendar ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter calendar ID (e.g., primary or calendar@gmail.com)',
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Create Event Fields
|
||||
@@ -220,9 +218,23 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { credential, operation, attendees, replaceExisting, ...rest } = params
|
||||
const {
|
||||
credential,
|
||||
operation,
|
||||
attendees,
|
||||
replaceExisting,
|
||||
calendarId,
|
||||
manualCalendarId,
|
||||
...rest
|
||||
} = params
|
||||
|
||||
const processedParams = { ...rest }
|
||||
// Handle calendar ID (selector or manual)
|
||||
const effectiveCalendarId = (calendarId || manualCalendarId || '').trim()
|
||||
|
||||
const processedParams: Record<string, any> = {
|
||||
...rest,
|
||||
calendarId: effectiveCalendarId || 'primary',
|
||||
}
|
||||
|
||||
// Convert comma-separated attendees string to array, only if it has content
|
||||
if (attendees && typeof attendees === 'string' && attendees.trim().length > 0) {
|
||||
@@ -258,6 +270,7 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
|
||||
operation: { type: 'string', required: true },
|
||||
credential: { type: 'string', required: true },
|
||||
calendarId: { type: 'string', required: false },
|
||||
manualCalendarId: { type: 'string', required: false },
|
||||
|
||||
// Create operation inputs
|
||||
summary: { type: 'string', required: false },
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import { GoogleDocsIcon } from '@/components/icons'
|
||||
import type {
|
||||
GoogleDocsCreateResponse,
|
||||
GoogleDocsReadResponse,
|
||||
GoogleDocsWriteResponse,
|
||||
} from '@/tools/google_docs/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type GoogleDocsResponse =
|
||||
| GoogleDocsReadResponse
|
||||
| GoogleDocsWriteResponse
|
||||
| GoogleDocsCreateResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { GoogleDocsResponse } from '@/tools/google_docs/types'
|
||||
|
||||
export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
|
||||
type: 'google_docs',
|
||||
@@ -45,7 +36,7 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
|
||||
requiredScopes: ['https://www.googleapis.com/auth/drive.file'],
|
||||
placeholder: 'Select Google account',
|
||||
},
|
||||
// Document Selector for read operation
|
||||
// Document selector (basic mode)
|
||||
{
|
||||
id: 'documentId',
|
||||
title: 'Select Document',
|
||||
@@ -56,38 +47,18 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
|
||||
requiredScopes: [],
|
||||
mimeType: 'application/vnd.google-apps.document',
|
||||
placeholder: 'Select a document',
|
||||
condition: { field: 'operation', value: 'read' },
|
||||
mode: 'basic',
|
||||
condition: { field: 'operation', value: ['read', 'write'] },
|
||||
},
|
||||
// Document Selector for write operation
|
||||
{
|
||||
id: 'documentId',
|
||||
title: 'Select Document',
|
||||
type: 'file-selector',
|
||||
layout: 'full',
|
||||
provider: 'google-drive',
|
||||
serviceId: 'google-drive',
|
||||
requiredScopes: [],
|
||||
mimeType: 'application/vnd.google-apps.document',
|
||||
placeholder: 'Select a document',
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
},
|
||||
// Manual Document ID for read operation
|
||||
// Manual document ID input (advanced mode)
|
||||
{
|
||||
id: 'manualDocumentId',
|
||||
title: 'Or Enter Document ID Manually',
|
||||
title: 'Document ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'ID of the document',
|
||||
condition: { field: 'operation', value: 'read' },
|
||||
},
|
||||
// Manual Document ID for write operation
|
||||
{
|
||||
id: 'manualDocumentId',
|
||||
title: 'Or Enter Document ID Manually',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'ID of the document',
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
placeholder: 'Enter document ID',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: ['read', 'write'] },
|
||||
},
|
||||
// Create-specific Fields
|
||||
{
|
||||
@@ -98,7 +69,7 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
|
||||
placeholder: 'Enter title for the new document',
|
||||
condition: { field: 'operation', value: 'create' },
|
||||
},
|
||||
// Folder Selector for create operation
|
||||
// Folder selector (basic mode)
|
||||
{
|
||||
id: 'folderSelector',
|
||||
title: 'Select Parent Folder',
|
||||
@@ -109,15 +80,17 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
|
||||
requiredScopes: [],
|
||||
mimeType: 'application/vnd.google-apps.folder',
|
||||
placeholder: 'Select a parent folder',
|
||||
mode: 'basic',
|
||||
condition: { field: 'operation', value: 'create' },
|
||||
},
|
||||
// Manual Folder ID for create operation
|
||||
// Manual folder ID input (advanced mode)
|
||||
{
|
||||
id: 'folderId',
|
||||
title: 'Or Enter Parent Folder ID Manually',
|
||||
title: 'Parent Folder ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'ID of the parent folder (leave empty for root folder)',
|
||||
placeholder: 'Enter parent folder ID (leave empty for root folder)',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: 'create' },
|
||||
},
|
||||
// Content Field for write operation
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import { GoogleDriveIcon } from '@/components/icons'
|
||||
import type {
|
||||
GoogleDriveGetContentResponse,
|
||||
GoogleDriveListResponse,
|
||||
GoogleDriveUploadResponse,
|
||||
} from '@/tools/google_drive/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type GoogleDriveResponse =
|
||||
| GoogleDriveUploadResponse
|
||||
| GoogleDriveGetContentResponse
|
||||
| GoogleDriveListResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { GoogleDriveResponse } from '@/tools/google_drive/types'
|
||||
|
||||
export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
|
||||
type: 'google_drive',
|
||||
@@ -87,18 +78,17 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
|
||||
requiredScopes: ['https://www.googleapis.com/auth/drive.file'],
|
||||
mimeType: 'application/vnd.google-apps.folder',
|
||||
placeholder: 'Select a parent folder',
|
||||
mode: 'basic',
|
||||
condition: { field: 'operation', value: 'upload' },
|
||||
},
|
||||
{
|
||||
id: 'folderId',
|
||||
title: 'Or Enter Parent Folder ID Manually',
|
||||
id: 'manualFolderId',
|
||||
title: 'Parent Folder ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'ID of the parent folder (leave empty for root folder)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'upload',
|
||||
},
|
||||
placeholder: 'Enter parent folder ID (leave empty for root folder)',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: 'upload' },
|
||||
},
|
||||
// Get Content Fields
|
||||
// {
|
||||
@@ -160,21 +150,20 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
|
||||
requiredScopes: ['https://www.googleapis.com/auth/drive.file'],
|
||||
mimeType: 'application/vnd.google-apps.folder',
|
||||
placeholder: 'Select a parent folder',
|
||||
mode: 'basic',
|
||||
condition: { field: 'operation', value: 'create_folder' },
|
||||
},
|
||||
// Manual Folder ID input (shown only when no folder is selected)
|
||||
// Manual Folder ID input (advanced mode)
|
||||
{
|
||||
id: 'folderId',
|
||||
title: 'Or Enter Parent Folder ID Manually',
|
||||
id: 'manualFolderId',
|
||||
title: 'Parent Folder ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'ID of the parent folder (leave empty for root folder)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'create_folder',
|
||||
},
|
||||
placeholder: 'Enter parent folder ID (leave empty for root folder)',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: 'create_folder' },
|
||||
},
|
||||
// List Fields - Folder Selector
|
||||
// List Fields - Folder Selector (basic mode)
|
||||
{
|
||||
id: 'folderSelector',
|
||||
title: 'Select Folder',
|
||||
@@ -185,19 +174,18 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
|
||||
requiredScopes: ['https://www.googleapis.com/auth/drive.file'],
|
||||
mimeType: 'application/vnd.google-apps.folder',
|
||||
placeholder: 'Select a folder to list files from',
|
||||
mode: 'basic',
|
||||
condition: { field: 'operation', value: 'list' },
|
||||
},
|
||||
// Manual Folder ID input (shown only when no folder is selected)
|
||||
// Manual Folder ID input (advanced mode)
|
||||
{
|
||||
id: 'folderId',
|
||||
title: 'Or Enter Folder ID Manually',
|
||||
id: 'manualFolderId',
|
||||
title: 'Folder ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'ID of the folder to list (leave empty for root folder)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'list',
|
||||
},
|
||||
placeholder: 'Enter folder ID (leave empty for root folder)',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: 'list' },
|
||||
},
|
||||
{
|
||||
id: 'query',
|
||||
@@ -234,14 +222,14 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { credential, folderId, folderSelector, mimeType, ...rest } = params
|
||||
const { credential, folderSelector, manualFolderId, mimeType, ...rest } = params
|
||||
|
||||
// Use folderSelector if provided, otherwise use folderId
|
||||
const effectiveFolderId = folderSelector || folderId || ''
|
||||
// Use folderSelector if provided, otherwise use manualFolderId
|
||||
const effectiveFolderId = (folderSelector || manualFolderId || '').trim()
|
||||
|
||||
return {
|
||||
accessToken: credential,
|
||||
folderId: effectiveFolderId.trim(),
|
||||
folderId: effectiveFolderId,
|
||||
pageSize: rest.pageSize ? Number.parseInt(rest.pageSize as string, 10) : undefined,
|
||||
mimeType: mimeType,
|
||||
...rest,
|
||||
@@ -259,8 +247,8 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
|
||||
// Get Content operation inputs
|
||||
// fileId: { type: 'string', required: false },
|
||||
// List operation inputs
|
||||
folderId: { type: 'string', required: false },
|
||||
folderSelector: { type: 'string', required: false },
|
||||
manualFolderId: { type: 'string', required: false },
|
||||
query: { type: 'string', required: false },
|
||||
pageSize: { type: 'number', required: false },
|
||||
},
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
import { GoogleSheetsIcon } from '@/components/icons'
|
||||
import type {
|
||||
GoogleSheetsAppendResponse,
|
||||
GoogleSheetsReadResponse,
|
||||
GoogleSheetsUpdateResponse,
|
||||
GoogleSheetsWriteResponse,
|
||||
} from '@/tools/google_sheets/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type GoogleSheetsResponse =
|
||||
| GoogleSheetsReadResponse
|
||||
| GoogleSheetsWriteResponse
|
||||
| GoogleSheetsUpdateResponse
|
||||
| GoogleSheetsAppendResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { GoogleSheetsResponse } from '@/tools/google_sheets/types'
|
||||
|
||||
export const GoogleSheetsBlock: BlockConfig<GoogleSheetsResponse> = {
|
||||
type: 'google_sheets',
|
||||
@@ -59,15 +48,16 @@ export const GoogleSheetsBlock: BlockConfig<GoogleSheetsResponse> = {
|
||||
requiredScopes: [],
|
||||
mimeType: 'application/vnd.google-apps.spreadsheet',
|
||||
placeholder: 'Select a spreadsheet',
|
||||
mode: 'basic',
|
||||
},
|
||||
// Manual Spreadsheet ID (hidden by default)
|
||||
// Manual Spreadsheet ID (advanced mode)
|
||||
{
|
||||
id: 'manualSpreadsheetId',
|
||||
title: 'Or Enter Spreadsheet ID Manually',
|
||||
title: 'Spreadsheet ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'ID of the spreadsheet (from URL)',
|
||||
condition: { field: 'spreadsheetId', value: '' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
// Range
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { HuggingFaceIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { HuggingFaceChatResponse } from '@/tools/huggingface/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const HuggingFaceBlock: BlockConfig<HuggingFaceChatResponse> = {
|
||||
type: 'huggingface',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ImageIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { DalleResponse } from '@/tools/openai/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const ImageGeneratorBlock: BlockConfig<DalleResponse> = {
|
||||
type: 'image_generator',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { JinaAIIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { ReadUrlResponse } from '@/tools/jina/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const JinaBlock: BlockConfig<ReadUrlResponse> = {
|
||||
type: 'jina',
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
import { JiraIcon } from '@/components/icons'
|
||||
import type {
|
||||
JiraRetrieveResponse,
|
||||
JiraRetrieveResponseBulk,
|
||||
JiraUpdateResponse,
|
||||
JiraWriteResponse,
|
||||
} from '@/tools/jira/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type JiraResponse =
|
||||
| JiraRetrieveResponse
|
||||
| JiraUpdateResponse
|
||||
| JiraWriteResponse
|
||||
| JiraRetrieveResponseBulk
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { JiraResponse } from '@/tools/jira/types'
|
||||
|
||||
export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
type: 'jira',
|
||||
@@ -24,7 +13,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
bgColor: '#E0E0E0',
|
||||
icon: JiraIcon,
|
||||
subBlocks: [
|
||||
// Operation selector
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
@@ -62,7 +50,7 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
],
|
||||
placeholder: 'Select Jira account',
|
||||
},
|
||||
// Use file-selector component for issue selection
|
||||
// Project selector (basic mode)
|
||||
{
|
||||
id: 'projectId',
|
||||
title: 'Select Project',
|
||||
@@ -71,7 +59,18 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
provider: 'jira',
|
||||
serviceId: 'jira',
|
||||
placeholder: 'Select Jira project',
|
||||
mode: 'basic',
|
||||
},
|
||||
// Manual project ID input (advanced mode)
|
||||
{
|
||||
id: 'manualProjectId',
|
||||
title: 'Project ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Jira project ID',
|
||||
mode: 'advanced',
|
||||
},
|
||||
// Issue selector (basic mode)
|
||||
{
|
||||
id: 'issueKey',
|
||||
title: 'Select Issue',
|
||||
@@ -81,6 +80,17 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
serviceId: 'jira',
|
||||
placeholder: 'Select Jira issue',
|
||||
condition: { field: 'operation', value: ['read', 'update'] },
|
||||
mode: 'basic',
|
||||
},
|
||||
// Manual issue key input (advanced mode)
|
||||
{
|
||||
id: 'manualIssueKey',
|
||||
title: 'Issue Key',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Jira issue key',
|
||||
condition: { field: 'operation', value: ['read', 'update'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'summary',
|
||||
@@ -117,18 +127,32 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { credential, projectId, manualProjectId, issueKey, manualIssueKey, ...rest } = params
|
||||
|
||||
// Base params that are always needed
|
||||
const baseParams = {
|
||||
accessToken: params.credential,
|
||||
accessToken: credential,
|
||||
domain: params.domain,
|
||||
}
|
||||
|
||||
// Use the selected project ID or the manually entered one
|
||||
const effectiveProjectId = (projectId || manualProjectId || '').trim()
|
||||
|
||||
// Use the selected issue key or the manually entered one
|
||||
const effectiveIssueKey = (issueKey || manualIssueKey || '').trim()
|
||||
|
||||
// Define allowed parameters for each operation
|
||||
switch (params.operation) {
|
||||
case 'write': {
|
||||
if (!effectiveProjectId) {
|
||||
throw new Error(
|
||||
'Project ID is required. Please select a project or enter a project ID manually.'
|
||||
)
|
||||
}
|
||||
|
||||
// For write operations, only include write-specific fields
|
||||
const writeParams = {
|
||||
projectId: params.projectId,
|
||||
projectId: effectiveProjectId,
|
||||
summary: params.summary || '',
|
||||
description: params.description || '',
|
||||
issueType: params.issueType || 'Task',
|
||||
@@ -141,10 +165,21 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
}
|
||||
}
|
||||
case 'update': {
|
||||
if (!effectiveProjectId) {
|
||||
throw new Error(
|
||||
'Project ID is required. Please select a project or enter a project ID manually.'
|
||||
)
|
||||
}
|
||||
if (!effectiveIssueKey) {
|
||||
throw new Error(
|
||||
'Issue Key is required. Please select an issue or enter an issue key manually.'
|
||||
)
|
||||
}
|
||||
|
||||
// For update operations, only include update-specific fields
|
||||
const updateParams = {
|
||||
projectId: params.projectId,
|
||||
issueKey: params.issueKey,
|
||||
projectId: effectiveProjectId,
|
||||
issueKey: effectiveIssueKey,
|
||||
summary: params.summary || '',
|
||||
description: params.description || '',
|
||||
}
|
||||
@@ -155,17 +190,29 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
}
|
||||
}
|
||||
case 'read': {
|
||||
if (!effectiveIssueKey) {
|
||||
throw new Error(
|
||||
'Issue Key is required. Please select an issue or enter an issue key manually.'
|
||||
)
|
||||
}
|
||||
|
||||
// For read operations, only include read-specific fields
|
||||
return {
|
||||
...baseParams,
|
||||
issueKey: params.issueKey,
|
||||
issueKey: effectiveIssueKey,
|
||||
}
|
||||
}
|
||||
case 'read-bulk': {
|
||||
if (!effectiveProjectId) {
|
||||
throw new Error(
|
||||
'Project ID is required. Please select a project or enter a project ID manually.'
|
||||
)
|
||||
}
|
||||
|
||||
// For read-bulk operations, only include read-bulk-specific fields
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId,
|
||||
projectId: effectiveProjectId,
|
||||
}
|
||||
}
|
||||
default:
|
||||
@@ -178,8 +225,10 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
operation: { type: 'string', required: true },
|
||||
domain: { type: 'string', required: true },
|
||||
credential: { type: 'string', required: true },
|
||||
issueKey: { type: 'string', required: true },
|
||||
issueKey: { type: 'string', required: false },
|
||||
projectId: { type: 'string', required: false },
|
||||
manualProjectId: { type: 'string', required: false },
|
||||
manualIssueKey: { type: 'string', required: false },
|
||||
// Update operation inputs
|
||||
summary: { type: 'string', required: true },
|
||||
description: { type: 'string', required: false },
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PackageSearchIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '../types'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
|
||||
export const KnowledgeBlock: BlockConfig = {
|
||||
type: 'knowledge',
|
||||
@@ -26,6 +26,25 @@ export const KnowledgeBlock: BlockConfig = {
|
||||
return 'knowledge_search'
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
// Validate required fields for each operation
|
||||
if (params.operation === 'search' && !params.knowledgeBaseIds) {
|
||||
throw new Error('Knowledge base IDs are required for search operation')
|
||||
}
|
||||
if (
|
||||
(params.operation === 'upload_chunk' || params.operation === 'create_document') &&
|
||||
!params.knowledgeBaseId
|
||||
) {
|
||||
throw new Error(
|
||||
'Knowledge base ID is required for upload_chunk and create_document operations'
|
||||
)
|
||||
}
|
||||
if (params.operation === 'upload_chunk' && !params.documentId) {
|
||||
throw new Error('Document ID is required for upload_chunk operation')
|
||||
}
|
||||
|
||||
return params
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import type { LinearCreateIssueResponse, LinearReadIssuesResponse } from '@/tools/linear/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type LinearResponse = LinearReadIssuesResponse | LinearCreateIssueResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { LinearResponse } from '@/tools/linear/types'
|
||||
|
||||
export const LinearBlock: BlockConfig<LinearResponse> = {
|
||||
type: 'linear',
|
||||
@@ -42,6 +40,7 @@ export const LinearBlock: BlockConfig<LinearResponse> = {
|
||||
provider: 'linear',
|
||||
serviceId: 'linear',
|
||||
placeholder: 'Select a team',
|
||||
mode: 'basic',
|
||||
},
|
||||
{
|
||||
id: 'projectId',
|
||||
@@ -51,6 +50,25 @@ export const LinearBlock: BlockConfig<LinearResponse> = {
|
||||
provider: 'linear',
|
||||
serviceId: 'linear',
|
||||
placeholder: 'Select a project',
|
||||
mode: 'basic',
|
||||
},
|
||||
// Manual team ID input (advanced mode)
|
||||
{
|
||||
id: 'manualTeamId',
|
||||
title: 'Team ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Linear team ID',
|
||||
mode: 'advanced',
|
||||
},
|
||||
// Manual project ID input (advanced mode)
|
||||
{
|
||||
id: 'manualProjectId',
|
||||
title: 'Project ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Linear project ID',
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'title',
|
||||
@@ -73,19 +91,40 @@ export const LinearBlock: BlockConfig<LinearResponse> = {
|
||||
tool: (params) =>
|
||||
params.operation === 'write' ? 'linear_create_issue' : 'linear_read_issues',
|
||||
params: (params) => {
|
||||
// Handle team ID (selector or manual)
|
||||
const effectiveTeamId = (params.teamId || params.manualTeamId || '').trim()
|
||||
|
||||
// Handle project ID (selector or manual)
|
||||
const effectiveProjectId = (params.projectId || params.manualProjectId || '').trim()
|
||||
|
||||
if (!effectiveTeamId) {
|
||||
throw new Error('Team ID is required. Please select a team or enter a team ID manually.')
|
||||
}
|
||||
if (!effectiveProjectId) {
|
||||
throw new Error(
|
||||
'Project ID is required. Please select a project or enter a project ID manually.'
|
||||
)
|
||||
}
|
||||
|
||||
if (params.operation === 'write') {
|
||||
if (!params.title?.trim()) {
|
||||
throw new Error('Title is required for creating issues.')
|
||||
}
|
||||
if (!params.description?.trim()) {
|
||||
throw new Error('Description is required for creating issues.')
|
||||
}
|
||||
return {
|
||||
credential: params.credential,
|
||||
teamId: params.teamId,
|
||||
projectId: params.projectId,
|
||||
teamId: effectiveTeamId,
|
||||
projectId: effectiveProjectId,
|
||||
title: params.title,
|
||||
description: params.description,
|
||||
}
|
||||
}
|
||||
return {
|
||||
credential: params.credential,
|
||||
teamId: params.teamId,
|
||||
projectId: params.projectId,
|
||||
teamId: effectiveTeamId,
|
||||
projectId: effectiveProjectId,
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -93,8 +132,10 @@ export const LinearBlock: BlockConfig<LinearResponse> = {
|
||||
inputs: {
|
||||
operation: { type: 'string', required: true },
|
||||
credential: { type: 'string', required: true },
|
||||
teamId: { type: 'string', required: true },
|
||||
projectId: { type: 'string', required: true },
|
||||
teamId: { type: 'string', required: false },
|
||||
projectId: { type: 'string', required: false },
|
||||
manualTeamId: { type: 'string', required: false },
|
||||
manualProjectId: { type: 'string', required: false },
|
||||
title: { type: 'string', required: false },
|
||||
description: { type: 'string', required: false },
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { LinkupIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { LinkupSearchToolResponse } from '@/tools/linkup/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const LinkupBlock: BlockConfig<LinkupSearchToolResponse> = {
|
||||
type: 'linkup',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Mem0Icon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { Mem0Response } from '@/tools/mem0/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const Mem0Block: BlockConfig<Mem0Response> = {
|
||||
type: 'mem0',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BrainIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '../types'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
|
||||
export const MemoryBlock: BlockConfig = {
|
||||
type: 'memory',
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import { MicrosoftExcelIcon } from '@/components/icons'
|
||||
import type {
|
||||
MicrosoftExcelReadResponse,
|
||||
MicrosoftExcelTableAddResponse,
|
||||
MicrosoftExcelWriteResponse,
|
||||
} from '@/tools/microsoft_excel/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type MicrosoftExcelResponse =
|
||||
| MicrosoftExcelReadResponse
|
||||
| MicrosoftExcelWriteResponse
|
||||
| MicrosoftExcelTableAddResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { MicrosoftExcelResponse } from '@/tools/microsoft_excel/types'
|
||||
|
||||
export const MicrosoftExcelBlock: BlockConfig<MicrosoftExcelResponse> = {
|
||||
type: 'microsoft_excel',
|
||||
@@ -53,14 +44,15 @@ export const MicrosoftExcelBlock: BlockConfig<MicrosoftExcelResponse> = {
|
||||
requiredScopes: [],
|
||||
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
placeholder: 'Select a spreadsheet',
|
||||
mode: 'basic',
|
||||
},
|
||||
{
|
||||
id: 'manualSpreadsheetId',
|
||||
title: 'Or Enter Spreadsheet ID Manually',
|
||||
title: 'Spreadsheet ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'ID of the spreadsheet (from URL)',
|
||||
condition: { field: 'spreadsheetId', value: '' },
|
||||
placeholder: 'Enter spreadsheet ID',
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'range',
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import { MicrosoftTeamsIcon } from '@/components/icons'
|
||||
import type {
|
||||
MicrosoftTeamsReadResponse,
|
||||
MicrosoftTeamsWriteResponse,
|
||||
} from '@/tools/microsoft_teams/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type MicrosoftTeamsResponse = MicrosoftTeamsReadResponse | MicrosoftTeamsWriteResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { MicrosoftTeamsResponse } from '@/tools/microsoft_teams/types'
|
||||
|
||||
export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
type: 'microsoft_teams',
|
||||
@@ -64,6 +59,16 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
serviceId: 'microsoft-teams',
|
||||
requiredScopes: [],
|
||||
placeholder: 'Select a team',
|
||||
mode: 'basic',
|
||||
condition: { field: 'operation', value: ['read_channel', 'write_channel'] },
|
||||
},
|
||||
{
|
||||
id: 'manualTeamId',
|
||||
title: 'Team ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter team ID',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: ['read_channel', 'write_channel'] },
|
||||
},
|
||||
{
|
||||
@@ -75,6 +80,16 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
serviceId: 'microsoft-teams',
|
||||
requiredScopes: [],
|
||||
placeholder: 'Select a chat',
|
||||
mode: 'basic',
|
||||
condition: { field: 'operation', value: ['read_chat', 'write_chat'] },
|
||||
},
|
||||
{
|
||||
id: 'manualChatId',
|
||||
title: 'Chat ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter chat ID',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: ['read_chat', 'write_chat'] },
|
||||
},
|
||||
{
|
||||
@@ -86,6 +101,16 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
serviceId: 'microsoft-teams',
|
||||
requiredScopes: [],
|
||||
placeholder: 'Select a channel',
|
||||
mode: 'basic',
|
||||
condition: { field: 'operation', value: ['read_channel', 'write_channel'] },
|
||||
},
|
||||
{
|
||||
id: 'manualChannelId',
|
||||
title: 'Channel ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter channel ID',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: ['read_channel', 'write_channel'] },
|
||||
},
|
||||
// Create-specific Fields
|
||||
@@ -121,7 +146,22 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { credential, operation, ...rest } = params
|
||||
const {
|
||||
credential,
|
||||
operation,
|
||||
teamId,
|
||||
manualTeamId,
|
||||
chatId,
|
||||
manualChatId,
|
||||
channelId,
|
||||
manualChannelId,
|
||||
...rest
|
||||
} = params
|
||||
|
||||
// Use the selected IDs or the manually entered ones
|
||||
const effectiveTeamId = (teamId || manualTeamId || '').trim()
|
||||
const effectiveChatId = (chatId || manualChatId || '').trim()
|
||||
const effectiveChannelId = (channelId || manualChannelId || '').trim()
|
||||
|
||||
// Build the parameters based on operation type
|
||||
const baseParams = {
|
||||
@@ -131,27 +171,33 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
|
||||
// For chat operations, we need chatId
|
||||
if (operation === 'read_chat' || operation === 'write_chat') {
|
||||
if (!params.chatId) {
|
||||
throw new Error('Chat ID is required for chat operations')
|
||||
if (!effectiveChatId) {
|
||||
throw new Error(
|
||||
'Chat ID is required for chat operations. Please select a chat or enter a chat ID manually.'
|
||||
)
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
chatId: params.chatId,
|
||||
chatId: effectiveChatId,
|
||||
}
|
||||
}
|
||||
|
||||
// For channel operations, we need teamId and channelId
|
||||
if (operation === 'read_channel' || operation === 'write_channel') {
|
||||
if (!params.teamId) {
|
||||
throw new Error('Team ID is required for channel operations')
|
||||
if (!effectiveTeamId) {
|
||||
throw new Error(
|
||||
'Team ID is required for channel operations. Please select a team or enter a team ID manually.'
|
||||
)
|
||||
}
|
||||
if (!params.channelId) {
|
||||
throw new Error('Channel ID is required for channel operations')
|
||||
if (!effectiveChannelId) {
|
||||
throw new Error(
|
||||
'Channel ID is required for channel operations. Please select a channel or enter a channel ID manually.'
|
||||
)
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
teamId: params.teamId,
|
||||
channelId: params.channelId,
|
||||
teamId: effectiveTeamId,
|
||||
channelId: effectiveChannelId,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,11 +208,14 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
inputs: {
|
||||
operation: { type: 'string', required: true },
|
||||
credential: { type: 'string', required: true },
|
||||
messageId: { type: 'string', required: true },
|
||||
chatId: { type: 'string', required: true },
|
||||
channelId: { type: 'string', required: true },
|
||||
teamId: { type: 'string', required: true },
|
||||
content: { type: 'string', required: true },
|
||||
messageId: { type: 'string', required: false },
|
||||
chatId: { type: 'string', required: false },
|
||||
manualChatId: { type: 'string', required: false },
|
||||
channelId: { type: 'string', required: false },
|
||||
manualChannelId: { type: 'string', required: false },
|
||||
teamId: { type: 'string', required: false },
|
||||
manualTeamId: { type: 'string', required: false },
|
||||
content: { type: 'string', required: false },
|
||||
},
|
||||
outputs: {
|
||||
content: 'string',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { MistralIcon } from '@/components/icons'
|
||||
import { isProd } from '@/lib/environment'
|
||||
import type { BlockConfig, SubBlockConfig, SubBlockLayout, SubBlockType } from '@/blocks/types'
|
||||
import type { MistralParserOutput } from '@/tools/mistral/types'
|
||||
import type { BlockConfig, SubBlockConfig, SubBlockLayout, SubBlockType } from '../types'
|
||||
|
||||
const shouldEnableFileUpload = isProd
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NotionIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { NotionResponse } from '@/tools/notion/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const NotionBlock: BlockConfig<NotionResponse> = {
|
||||
type: 'notion',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { OpenAIIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '../types'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
|
||||
export const OpenAIBlock: BlockConfig = {
|
||||
type: 'openai',
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import { OutlookIcon } from '@/components/icons'
|
||||
import type {
|
||||
OutlookDraftResponse,
|
||||
OutlookReadResponse,
|
||||
OutlookSendResponse,
|
||||
} from '@/tools/outlook/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { OutlookResponse } from '@/tools/outlook/types'
|
||||
|
||||
export const OutlookBlock: BlockConfig<
|
||||
OutlookReadResponse | OutlookSendResponse | OutlookDraftResponse
|
||||
> = {
|
||||
export const OutlookBlock: BlockConfig<OutlookResponse> = {
|
||||
type: 'outlook',
|
||||
name: 'Outlook',
|
||||
description: 'Access Outlook',
|
||||
@@ -19,7 +13,6 @@ export const OutlookBlock: BlockConfig<
|
||||
bgColor: '#E0E0E0',
|
||||
icon: OutlookIcon,
|
||||
subBlocks: [
|
||||
// Operation selector
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
@@ -31,7 +24,6 @@ export const OutlookBlock: BlockConfig<
|
||||
{ label: 'Read Email', id: 'read_outlook' },
|
||||
],
|
||||
},
|
||||
// Gmail Credentials
|
||||
{
|
||||
id: 'credential',
|
||||
title: 'Microsoft Account',
|
||||
@@ -51,7 +43,6 @@ export const OutlookBlock: BlockConfig<
|
||||
],
|
||||
placeholder: 'Select Microsoft account',
|
||||
},
|
||||
// Send Email Fields
|
||||
{
|
||||
id: 'to',
|
||||
title: 'To',
|
||||
@@ -76,7 +67,7 @@ export const OutlookBlock: BlockConfig<
|
||||
placeholder: 'Email content',
|
||||
condition: { field: 'operation', value: ['send_outlook', 'draft_outlook'] },
|
||||
},
|
||||
// Read Email Fields - Add folder selector
|
||||
// Read Email Fields - Add folder selector (basic mode)
|
||||
{
|
||||
id: 'folder',
|
||||
title: 'Folder',
|
||||
@@ -86,6 +77,17 @@ export const OutlookBlock: BlockConfig<
|
||||
serviceId: 'outlook',
|
||||
requiredScopes: ['Mail.ReadWrite', 'Mail.ReadBasic', 'Mail.Read'],
|
||||
placeholder: 'Select Outlook folder',
|
||||
mode: 'basic',
|
||||
condition: { field: 'operation', value: 'read_outlook' },
|
||||
},
|
||||
// Manual folder input (advanced mode)
|
||||
{
|
||||
id: 'manualFolder',
|
||||
title: 'Folder',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Outlook folder name (e.g., INBOX, SENT, or custom folder)',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: 'read_outlook' },
|
||||
},
|
||||
{
|
||||
@@ -114,11 +116,14 @@ export const OutlookBlock: BlockConfig<
|
||||
},
|
||||
params: (params) => {
|
||||
// Pass the credential directly from the credential field
|
||||
const { credential, ...rest } = params
|
||||
const { credential, folder, manualFolder, ...rest } = params
|
||||
|
||||
// Handle folder input (selector or manual)
|
||||
const effectiveFolder = (folder || manualFolder || '').trim()
|
||||
|
||||
// Set default folder to INBOX if not specified
|
||||
if (rest.operation === 'read_outlook' && !rest.folder) {
|
||||
rest.folder = 'INBOX'
|
||||
if (rest.operation === 'read_outlook') {
|
||||
rest.folder = effectiveFolder || 'INBOX'
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -137,6 +142,7 @@ export const OutlookBlock: BlockConfig<
|
||||
body: { type: 'string', required: false },
|
||||
// Read operation inputs
|
||||
folder: { type: 'string', required: false },
|
||||
manualFolder: { type: 'string', required: false },
|
||||
maxResults: { type: 'number', required: false },
|
||||
},
|
||||
outputs: {
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
import { PerplexityIcon } from '@/components/icons'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
interface PerplexityChatResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
model: string
|
||||
usage: {
|
||||
prompt_tokens: number
|
||||
completion_tokens: number
|
||||
total_tokens: number
|
||||
}
|
||||
}
|
||||
}
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { PerplexityChatResponse } from '@/tools/perplexity/types'
|
||||
|
||||
export const PerplexityBlock: BlockConfig<PerplexityChatResponse> = {
|
||||
type: 'perplexity',
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { PineconeIcon } from '@/components/icons'
|
||||
// You'll need to create this icon
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { PineconeResponse } from '@/tools/pinecone/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const PineconeBlock: BlockConfig<PineconeResponse> = {
|
||||
type: 'pinecone',
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import { RedditIcon } from '@/components/icons'
|
||||
import type {
|
||||
RedditCommentsResponse,
|
||||
RedditHotPostsResponse,
|
||||
RedditPostsResponse,
|
||||
} from '@/tools/reddit/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { RedditResponse } from '@/tools/reddit/types'
|
||||
|
||||
export const RedditBlock: BlockConfig<
|
||||
RedditHotPostsResponse | RedditPostsResponse | RedditCommentsResponse
|
||||
> = {
|
||||
export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
type: 'reddit',
|
||||
name: 'Reddit',
|
||||
description: 'Access Reddit data and content',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ResponseIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { ResponseBlockOutput } from '@/tools/response/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const ResponseBlock: BlockConfig<ResponseBlockOutput> = {
|
||||
type: 'response',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ConnectIcon } from '@/components/icons'
|
||||
import { isHosted } from '@/lib/environment'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { ProviderId } from '@/providers/types'
|
||||
import { getAllModelProviders, getBaseModelProviders, getHostedModels } from '@/providers/utils'
|
||||
import { useOllamaStore } from '@/stores/ollama/store'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
interface RouterResponse extends ToolResponse {
|
||||
output: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { S3Icon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { S3Response } from '@/tools/s3/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const S3Block: BlockConfig<S3Response> = {
|
||||
type: 's3',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SerperIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { SearchResponse } from '@/tools/serper/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const SerperBlock: BlockConfig<SearchResponse> = {
|
||||
type: 'serper',
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { SlackIcon } from '@/components/icons'
|
||||
import type { SlackMessageResponse } from '@/tools/slack/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type SlackResponse = SlackMessageResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { SlackResponse } from '@/tools/slack/types'
|
||||
|
||||
export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
type: 'slack',
|
||||
@@ -76,6 +74,16 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
layout: 'full',
|
||||
provider: 'slack',
|
||||
placeholder: 'Select Slack channel',
|
||||
mode: 'basic',
|
||||
},
|
||||
// Manual channel ID input (advanced mode)
|
||||
{
|
||||
id: 'manualChannel',
|
||||
title: 'Channel ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Slack channel ID (e.g., C1234567890)',
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'text',
|
||||
@@ -97,10 +105,21 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { credential, authMethod, botToken, operation, ...rest } = params
|
||||
const { credential, authMethod, botToken, operation, channel, manualChannel, ...rest } =
|
||||
params
|
||||
|
||||
const baseParams = {
|
||||
// Handle channel input (selector or manual)
|
||||
const effectiveChannel = (channel || manualChannel || '').trim()
|
||||
|
||||
if (!effectiveChannel) {
|
||||
throw new Error(
|
||||
'Channel is required. Please select a channel or enter a channel ID manually.'
|
||||
)
|
||||
}
|
||||
|
||||
const baseParams: Record<string, any> = {
|
||||
...rest,
|
||||
channel: effectiveChannel,
|
||||
}
|
||||
|
||||
// Handle authentication based on method
|
||||
@@ -126,7 +145,8 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
authMethod: { type: 'string', required: true },
|
||||
credential: { type: 'string', required: false },
|
||||
botToken: { type: 'string', required: false },
|
||||
channel: { type: 'string', required: true },
|
||||
channel: { type: 'string', required: false },
|
||||
manualChannel: { type: 'string', required: false },
|
||||
text: { type: 'string', required: true },
|
||||
},
|
||||
outputs: {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { StagehandIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
interface StagehandExtractResponse extends ToolResponse {
|
||||
export interface StagehandExtractResponse extends ToolResponse {
|
||||
output: {
|
||||
data: Record<string, any>
|
||||
}
|
||||
|
||||
@@ -1,22 +1,6 @@
|
||||
import { StagehandIcon } from '@/components/icons'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
interface StagehandAgentResponse extends ToolResponse {
|
||||
output: {
|
||||
agentResult: {
|
||||
success: boolean
|
||||
completed: boolean
|
||||
message: string
|
||||
actions: Array<{
|
||||
type: string
|
||||
params: Record<string, any>
|
||||
result: Record<string, any>
|
||||
}>
|
||||
}
|
||||
structuredOutput?: Record<string, any>
|
||||
}
|
||||
}
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { StagehandAgentResponse } from '@/tools/stagehand/types'
|
||||
|
||||
export const StagehandAgentBlock: BlockConfig<StagehandAgentResponse> = {
|
||||
type: 'stagehand_agent',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { StartIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '../types'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
|
||||
export const StarterBlock: BlockConfig = {
|
||||
type: 'starter',
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
import { SupabaseIcon } from '@/components/icons'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
interface SupabaseResponse extends ToolResponse {
|
||||
output: {
|
||||
message: string
|
||||
results: any
|
||||
}
|
||||
error?: string
|
||||
}
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { SupabaseResponse } from '@/tools/supabase/types'
|
||||
|
||||
export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
type: 'supabase',
|
||||
@@ -21,7 +13,6 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
bgColor: '#1C1C1C',
|
||||
icon: SupabaseIcon,
|
||||
subBlocks: [
|
||||
// Operation selector
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
@@ -32,7 +23,6 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
{ label: 'Insert Rows', id: 'insert' },
|
||||
],
|
||||
},
|
||||
// Common Fields
|
||||
{
|
||||
id: 'projectId',
|
||||
title: 'Project ID',
|
||||
@@ -55,7 +45,6 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
placeholder: 'Your Supabase client anon key',
|
||||
password: true,
|
||||
},
|
||||
// Insert-specific Fields
|
||||
{
|
||||
id: 'data',
|
||||
title: 'Data',
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { TavilyIcon } from '@/components/icons'
|
||||
import type { TavilyExtractResponse, TavilySearchResponse } from '@/tools/tavily/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type TavilyResponse = TavilySearchResponse | TavilyExtractResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { TavilyResponse } from '@/tools/tavily/types'
|
||||
|
||||
export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
type: 'tavily',
|
||||
@@ -15,7 +13,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
bgColor: '#0066FF',
|
||||
icon: TavilyIcon,
|
||||
subBlocks: [
|
||||
// Operation selector
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
@@ -27,7 +24,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
],
|
||||
value: () => 'tavily_search',
|
||||
},
|
||||
// API Key (common)
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
@@ -36,7 +32,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
placeholder: 'Enter your Tavily API key',
|
||||
password: true,
|
||||
},
|
||||
// Search operation inputs
|
||||
{
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
@@ -53,7 +48,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
placeholder: '5',
|
||||
condition: { field: 'operation', value: 'tavily_search' },
|
||||
},
|
||||
// Extract operation inputs
|
||||
{
|
||||
id: 'urls',
|
||||
title: 'URL',
|
||||
@@ -93,10 +87,8 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
|
||||
inputs: {
|
||||
operation: { type: 'string', required: true },
|
||||
apiKey: { type: 'string', required: true },
|
||||
// Search operation
|
||||
query: { type: 'string', required: false },
|
||||
maxResults: { type: 'number', required: false },
|
||||
// Extract operation
|
||||
urls: { type: 'string', required: false },
|
||||
extract_depth: { type: 'string', required: false },
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { TelegramIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { TelegramMessageResponse } from '@/tools/telegram/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const TelegramBlock: BlockConfig<TelegramMessageResponse> = {
|
||||
type: 'telegram',
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import { BrainIcon } from '@/components/icons'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
interface ThinkingToolResponse extends ToolResponse {
|
||||
output: {
|
||||
acknowledgedThought: string
|
||||
}
|
||||
}
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { ThinkingToolResponse } from '@/tools/thinking/types'
|
||||
|
||||
export const ThinkingBlock: BlockConfig<ThinkingToolResponse> = {
|
||||
type: 'thinking',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TranslateIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { ProviderId } from '@/providers/types'
|
||||
import { getBaseModelProviders } from '@/providers/utils'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
const getTranslationPrompt = (
|
||||
targetLanguage: string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { TwilioIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { TwilioSMSBlockOutput } from '@/tools/twilio/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const TwilioSMSBlock: BlockConfig<TwilioSMSBlockOutput> = {
|
||||
type: 'twilio_sms',
|
||||
|
||||
@@ -1,78 +1,6 @@
|
||||
import { TypeformIcon } from '@/components/icons'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
interface TypeformResponse extends ToolResponse {
|
||||
output:
|
||||
| {
|
||||
total_items: number
|
||||
page_count: number
|
||||
items: Array<{
|
||||
landing_id: string
|
||||
token: string
|
||||
landed_at: string
|
||||
submitted_at: string
|
||||
metadata: {
|
||||
user_agent: string
|
||||
platform: string
|
||||
referer: string
|
||||
network_id: string
|
||||
browser: string
|
||||
}
|
||||
answers: Array<{
|
||||
field: {
|
||||
id: string
|
||||
type: string
|
||||
ref: string
|
||||
}
|
||||
type: string
|
||||
[key: string]: any // For different answer types (text, boolean, number, etc.)
|
||||
}>
|
||||
hidden: Record<string, any>
|
||||
calculated: {
|
||||
score: number
|
||||
}
|
||||
variables: Array<{
|
||||
key: string
|
||||
type: string
|
||||
[key: string]: any // For different variable types
|
||||
}>
|
||||
}>
|
||||
}
|
||||
| {
|
||||
fileUrl: string
|
||||
contentType: string
|
||||
filename: string
|
||||
}
|
||||
| {
|
||||
fields: Array<{
|
||||
dropoffs: number
|
||||
id: string
|
||||
label: string
|
||||
ref: string
|
||||
title: string
|
||||
type: string
|
||||
views: number
|
||||
}>
|
||||
form: {
|
||||
platforms: Array<{
|
||||
average_time: number
|
||||
completion_rate: number
|
||||
platform: string
|
||||
responses_count: number
|
||||
total_visits: number
|
||||
unique_visits: number
|
||||
}>
|
||||
summary: {
|
||||
average_time: number
|
||||
completion_rate: number
|
||||
responses_count: number
|
||||
total_visits: number
|
||||
unique_visits: number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { TypeformResponse } from '@/tools/typeform/types'
|
||||
|
||||
export const TypeformBlock: BlockConfig<TypeformResponse> = {
|
||||
type: 'typeform',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EyeIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { VisionResponse } from '@/tools/vision/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const VisionBlock: BlockConfig<VisionResponse> = {
|
||||
type: 'vision',
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { WealthboxIcon } from '@/components/icons'
|
||||
import type { WealthboxReadResponse, WealthboxWriteResponse } from '@/tools/wealthbox/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type WealthboxResponse = WealthboxReadResponse | WealthboxWriteResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { WealthboxResponse } from '@/tools/wealthbox/types'
|
||||
|
||||
export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
|
||||
type: 'wealthbox',
|
||||
@@ -56,17 +54,37 @@ export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
|
||||
requiredScopes: ['login', 'data'],
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Contact ID',
|
||||
mode: 'basic',
|
||||
condition: { field: 'operation', value: ['read_contact', 'write_task', 'write_note'] },
|
||||
},
|
||||
{
|
||||
id: 'manualContactId',
|
||||
title: 'Contact ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Contact ID',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: ['read_contact', 'write_task', 'write_note'] },
|
||||
},
|
||||
{
|
||||
id: 'taskId',
|
||||
title: 'Select Task',
|
||||
type: 'short-input',
|
||||
type: 'file-selector',
|
||||
provider: 'wealthbox',
|
||||
serviceId: 'wealthbox',
|
||||
requiredScopes: ['login', 'data'],
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Task ID',
|
||||
mode: 'basic',
|
||||
condition: { field: 'operation', value: ['read_task'] },
|
||||
},
|
||||
{
|
||||
id: 'manualTaskId',
|
||||
title: 'Task ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Task ID',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: ['read_task'] },
|
||||
},
|
||||
{
|
||||
@@ -155,7 +173,14 @@ export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { credential, operation, ...rest } = params
|
||||
const { credential, operation, contactId, manualContactId, taskId, manualTaskId, ...rest } =
|
||||
params
|
||||
|
||||
// Handle contact ID input (selector or manual)
|
||||
const effectiveContactId = (contactId || manualContactId || '').trim()
|
||||
|
||||
// Handle task ID input (selector or manual)
|
||||
const effectiveTaskId = (taskId || manualTaskId || '').trim()
|
||||
|
||||
// Build the parameters based on operation type
|
||||
const baseParams = {
|
||||
@@ -168,25 +193,40 @@ export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
|
||||
return {
|
||||
...baseParams,
|
||||
noteId: params.noteId,
|
||||
contactId: effectiveContactId,
|
||||
}
|
||||
}
|
||||
|
||||
// For contact operations, we need contactId
|
||||
if (operation === 'read_contact') {
|
||||
if (!params.contactId) {
|
||||
if (!effectiveContactId) {
|
||||
throw new Error('Contact ID is required for contact operations')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
contactId: params.contactId,
|
||||
contactId: effectiveContactId,
|
||||
}
|
||||
}
|
||||
|
||||
// For task operations, we need taskId
|
||||
if (operation === 'read_task') {
|
||||
if (!effectiveTaskId) {
|
||||
throw new Error('Task ID is required for task operations')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
taskId: params.taskId,
|
||||
taskId: effectiveTaskId,
|
||||
}
|
||||
}
|
||||
|
||||
// For write_task and write_note operations, we need contactId
|
||||
if (operation === 'write_task' || operation === 'write_note') {
|
||||
if (!effectiveContactId) {
|
||||
throw new Error('Contact ID is required for this operation')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
contactId: effectiveContactId,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +239,9 @@ export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
|
||||
credential: { type: 'string', required: true },
|
||||
noteId: { type: 'string', required: false },
|
||||
contactId: { type: 'string', required: false },
|
||||
manualContactId: { type: 'string', required: false },
|
||||
taskId: { type: 'string', required: false },
|
||||
manualTaskId: { type: 'string', required: false },
|
||||
content: { type: 'string', required: false },
|
||||
firstName: { type: 'string', required: false },
|
||||
lastName: { type: 'string', required: false },
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
import { WhatsAppIcon } from '@/components/icons'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { WhatsAppResponse } from '@/tools/whatsapp/types'
|
||||
|
||||
interface WhatsAppBlockOutput extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
messageId?: string
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
export const WhatsAppBlock: BlockConfig<WhatsAppBlockOutput> = {
|
||||
export const WhatsAppBlock: BlockConfig<WhatsAppResponse> = {
|
||||
type: 'whatsapp',
|
||||
name: 'WhatsApp',
|
||||
description: 'Send WhatsApp messages',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { WorkflowIcon } from '@/components/icons'
|
||||
import { createLogger } from '@/lib/logs/console-logger'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
const logger = createLogger('WorkflowBlock')
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { xIcon } from '@/components/icons'
|
||||
import type { XReadResponse, XSearchResponse, XUserResponse, XWriteResponse } from '@/tools/x/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
type XResponse = XWriteResponse | XReadResponse | XSearchResponse | XUserResponse
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { XResponse } from '@/tools/x/types'
|
||||
|
||||
export const XBlock: BlockConfig<XResponse> = {
|
||||
type: 'x',
|
||||
@@ -15,7 +13,6 @@ export const XBlock: BlockConfig<XResponse> = {
|
||||
bgColor: '#000000', // X's black color
|
||||
icon: xIcon,
|
||||
subBlocks: [
|
||||
// Operation selector
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
@@ -29,7 +26,6 @@ export const XBlock: BlockConfig<XResponse> = {
|
||||
],
|
||||
value: () => 'x_write',
|
||||
},
|
||||
// X OAuth Authentication
|
||||
{
|
||||
id: 'credential',
|
||||
title: 'X Account',
|
||||
@@ -40,7 +36,6 @@ export const XBlock: BlockConfig<XResponse> = {
|
||||
requiredScopes: ['tweet.read', 'tweet.write', 'users.read'],
|
||||
placeholder: 'Select X account',
|
||||
},
|
||||
// Write operation inputs
|
||||
{
|
||||
id: 'text',
|
||||
title: 'Tweet Text',
|
||||
@@ -65,7 +60,6 @@ export const XBlock: BlockConfig<XResponse> = {
|
||||
placeholder: 'Enter comma-separated media IDs',
|
||||
condition: { field: 'operation', value: 'x_write' },
|
||||
},
|
||||
// Read operation inputs
|
||||
{
|
||||
id: 'tweetId',
|
||||
title: 'Tweet ID',
|
||||
@@ -86,7 +80,6 @@ export const XBlock: BlockConfig<XResponse> = {
|
||||
value: () => 'false',
|
||||
condition: { field: 'operation', value: 'x_read' },
|
||||
},
|
||||
// Search operation inputs
|
||||
{
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
@@ -131,7 +124,6 @@ export const XBlock: BlockConfig<XResponse> = {
|
||||
placeholder: 'YYYY-MM-DDTHH:mm:ssZ',
|
||||
condition: { field: 'operation', value: 'x_search' },
|
||||
},
|
||||
// User operation inputs
|
||||
{
|
||||
id: 'username',
|
||||
title: 'Username',
|
||||
@@ -198,21 +190,17 @@ export const XBlock: BlockConfig<XResponse> = {
|
||||
inputs: {
|
||||
operation: { type: 'string', required: true },
|
||||
credential: { type: 'string', required: true },
|
||||
// Write operation
|
||||
text: { type: 'string', required: false },
|
||||
replyTo: { type: 'string', required: false },
|
||||
mediaIds: { type: 'string', required: false },
|
||||
poll: { type: 'json', required: false },
|
||||
// Read operation
|
||||
tweetId: { type: 'string', required: false },
|
||||
includeReplies: { type: 'boolean', required: false },
|
||||
// Search operation
|
||||
query: { type: 'string', required: false },
|
||||
maxResults: { type: 'number', required: false },
|
||||
startTime: { type: 'string', required: false },
|
||||
endTime: { type: 'string', required: false },
|
||||
sortOrder: { type: 'string', required: false },
|
||||
// User operation
|
||||
username: { type: 'string', required: false },
|
||||
includeRecentTweets: { type: 'boolean', required: false },
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { YouTubeIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { YouTubeSearchResponse } from '@/tools/youtube/types'
|
||||
import type { BlockConfig } from '../types'
|
||||
|
||||
export const YouTubeBlock: BlockConfig<YouTubeSearchResponse> = {
|
||||
type: 'youtube',
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
getBlocksByCategory,
|
||||
isValidBlockType,
|
||||
registry,
|
||||
} from './registry'
|
||||
} from '@/blocks/registry'
|
||||
|
||||
export { registry, getBlock, getBlocksByCategory, getAllBlockTypes, isValidBlockType, getAllBlocks }
|
||||
|
||||
export type { BlockConfig } from './types'
|
||||
export type { BlockConfig } from '@/blocks/types'
|
||||
|
||||
@@ -3,67 +3,67 @@
|
||||
*
|
||||
*/
|
||||
// Import all blocks directly here
|
||||
import { AgentBlock } from './blocks/agent'
|
||||
import { AirtableBlock } from './blocks/airtable'
|
||||
import { ApiBlock } from './blocks/api'
|
||||
import { BrowserUseBlock } from './blocks/browser_use'
|
||||
import { ClayBlock } from './blocks/clay'
|
||||
import { ConditionBlock } from './blocks/condition'
|
||||
import { ConfluenceBlock } from './blocks/confluence'
|
||||
import { DiscordBlock } from './blocks/discord'
|
||||
import { ElevenLabsBlock } from './blocks/elevenlabs'
|
||||
import { EvaluatorBlock } from './blocks/evaluator'
|
||||
import { ExaBlock } from './blocks/exa'
|
||||
import { FileBlock } from './blocks/file'
|
||||
import { FirecrawlBlock } from './blocks/firecrawl'
|
||||
import { FunctionBlock } from './blocks/function'
|
||||
import { GitHubBlock } from './blocks/github'
|
||||
import { GmailBlock } from './blocks/gmail'
|
||||
import { GoogleSearchBlock } from './blocks/google'
|
||||
import { GoogleCalendarBlock } from './blocks/google_calendar'
|
||||
import { GoogleDocsBlock } from './blocks/google_docs'
|
||||
import { GoogleDriveBlock } from './blocks/google_drive'
|
||||
import { GoogleSheetsBlock } from './blocks/google_sheets'
|
||||
import { HuggingFaceBlock } from './blocks/huggingface'
|
||||
import { ImageGeneratorBlock } from './blocks/image_generator'
|
||||
import { JinaBlock } from './blocks/jina'
|
||||
import { JiraBlock } from './blocks/jira'
|
||||
import { KnowledgeBlock } from './blocks/knowledge'
|
||||
import { LinearBlock } from './blocks/linear'
|
||||
import { LinkupBlock } from './blocks/linkup'
|
||||
import { Mem0Block } from './blocks/mem0'
|
||||
import { MemoryBlock } from './blocks/memory'
|
||||
import { MicrosoftExcelBlock } from './blocks/microsoft_excel'
|
||||
import { MicrosoftTeamsBlock } from './blocks/microsoft_teams'
|
||||
import { MistralParseBlock } from './blocks/mistral_parse'
|
||||
import { NotionBlock } from './blocks/notion'
|
||||
import { OpenAIBlock } from './blocks/openai'
|
||||
import { OutlookBlock } from './blocks/outlook'
|
||||
import { PerplexityBlock } from './blocks/perplexity'
|
||||
import { PineconeBlock } from './blocks/pinecone'
|
||||
import { RedditBlock } from './blocks/reddit'
|
||||
import { ResponseBlock } from './blocks/response'
|
||||
import { RouterBlock } from './blocks/router'
|
||||
import { S3Block } from './blocks/s3'
|
||||
import { SerperBlock } from './blocks/serper'
|
||||
import { SlackBlock } from './blocks/slack'
|
||||
import { StagehandBlock } from './blocks/stagehand'
|
||||
import { StagehandAgentBlock } from './blocks/stagehand_agent'
|
||||
import { StarterBlock } from './blocks/starter'
|
||||
import { SupabaseBlock } from './blocks/supabase'
|
||||
import { TavilyBlock } from './blocks/tavily'
|
||||
import { TelegramBlock } from './blocks/telegram'
|
||||
import { ThinkingBlock } from './blocks/thinking'
|
||||
import { TranslateBlock } from './blocks/translate'
|
||||
import { TwilioSMSBlock } from './blocks/twilio'
|
||||
import { TypeformBlock } from './blocks/typeform'
|
||||
import { VisionBlock } from './blocks/vision'
|
||||
import { WealthboxBlock } from './blocks/wealthbox'
|
||||
import { WhatsAppBlock } from './blocks/whatsapp'
|
||||
import { WorkflowBlock } from './blocks/workflow'
|
||||
import { XBlock } from './blocks/x'
|
||||
import { YouTubeBlock } from './blocks/youtube'
|
||||
import type { BlockConfig } from './types'
|
||||
import { AgentBlock } from '@/blocks/blocks/agent'
|
||||
import { AirtableBlock } from '@/blocks/blocks/airtable'
|
||||
import { ApiBlock } from '@/blocks/blocks/api'
|
||||
import { BrowserUseBlock } from '@/blocks/blocks/browser_use'
|
||||
import { ClayBlock } from '@/blocks/blocks/clay'
|
||||
import { ConditionBlock } from '@/blocks/blocks/condition'
|
||||
import { ConfluenceBlock } from '@/blocks/blocks/confluence'
|
||||
import { DiscordBlock } from '@/blocks/blocks/discord'
|
||||
import { ElevenLabsBlock } from '@/blocks/blocks/elevenlabs'
|
||||
import { EvaluatorBlock } from '@/blocks/blocks/evaluator'
|
||||
import { ExaBlock } from '@/blocks/blocks/exa'
|
||||
import { FileBlock } from '@/blocks/blocks/file'
|
||||
import { FirecrawlBlock } from '@/blocks/blocks/firecrawl'
|
||||
import { FunctionBlock } from '@/blocks/blocks/function'
|
||||
import { GitHubBlock } from '@/blocks/blocks/github'
|
||||
import { GmailBlock } from '@/blocks/blocks/gmail'
|
||||
import { GoogleSearchBlock } from '@/blocks/blocks/google'
|
||||
import { GoogleCalendarBlock } from '@/blocks/blocks/google_calendar'
|
||||
import { GoogleDocsBlock } from '@/blocks/blocks/google_docs'
|
||||
import { GoogleDriveBlock } from '@/blocks/blocks/google_drive'
|
||||
import { GoogleSheetsBlock } from '@/blocks/blocks/google_sheets'
|
||||
import { HuggingFaceBlock } from '@/blocks/blocks/huggingface'
|
||||
import { ImageGeneratorBlock } from '@/blocks/blocks/image_generator'
|
||||
import { JinaBlock } from '@/blocks/blocks/jina'
|
||||
import { JiraBlock } from '@/blocks/blocks/jira'
|
||||
import { KnowledgeBlock } from '@/blocks/blocks/knowledge'
|
||||
import { LinearBlock } from '@/blocks/blocks/linear'
|
||||
import { LinkupBlock } from '@/blocks/blocks/linkup'
|
||||
import { Mem0Block } from '@/blocks/blocks/mem0'
|
||||
import { MemoryBlock } from '@/blocks/blocks/memory'
|
||||
import { MicrosoftExcelBlock } from '@/blocks/blocks/microsoft_excel'
|
||||
import { MicrosoftTeamsBlock } from '@/blocks/blocks/microsoft_teams'
|
||||
import { MistralParseBlock } from '@/blocks/blocks/mistral_parse'
|
||||
import { NotionBlock } from '@/blocks/blocks/notion'
|
||||
import { OpenAIBlock } from '@/blocks/blocks/openai'
|
||||
import { OutlookBlock } from '@/blocks/blocks/outlook'
|
||||
import { PerplexityBlock } from '@/blocks/blocks/perplexity'
|
||||
import { PineconeBlock } from '@/blocks/blocks/pinecone'
|
||||
import { RedditBlock } from '@/blocks/blocks/reddit'
|
||||
import { ResponseBlock } from '@/blocks/blocks/response'
|
||||
import { RouterBlock } from '@/blocks/blocks/router'
|
||||
import { S3Block } from '@/blocks/blocks/s3'
|
||||
import { SerperBlock } from '@/blocks/blocks/serper'
|
||||
import { SlackBlock } from '@/blocks/blocks/slack'
|
||||
import { StagehandBlock } from '@/blocks/blocks/stagehand'
|
||||
import { StagehandAgentBlock } from '@/blocks/blocks/stagehand_agent'
|
||||
import { StarterBlock } from '@/blocks/blocks/starter'
|
||||
import { SupabaseBlock } from '@/blocks/blocks/supabase'
|
||||
import { TavilyBlock } from '@/blocks/blocks/tavily'
|
||||
import { TelegramBlock } from '@/blocks/blocks/telegram'
|
||||
import { ThinkingBlock } from '@/blocks/blocks/thinking'
|
||||
import { TranslateBlock } from '@/blocks/blocks/translate'
|
||||
import { TwilioSMSBlock } from '@/blocks/blocks/twilio'
|
||||
import { TypeformBlock } from '@/blocks/blocks/typeform'
|
||||
import { VisionBlock } from '@/blocks/blocks/vision'
|
||||
import { WealthboxBlock } from '@/blocks/blocks/wealthbox'
|
||||
import { WhatsAppBlock } from '@/blocks/blocks/whatsapp'
|
||||
import { WorkflowBlock } from '@/blocks/blocks/workflow'
|
||||
import { XBlock } from '@/blocks/blocks/x'
|
||||
import { YouTubeBlock } from '@/blocks/blocks/youtube'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
|
||||
// Registry of all available blocks, alphabetically sorted
|
||||
export const registry: Record<string, BlockConfig> = {
|
||||
|
||||
@@ -2808,7 +2808,7 @@ export const ResponseIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
>
|
||||
<path
|
||||
d='m1030.975 188 81.249 81.249-429.228 429.228h300.747c516.223 0 936.257 420.034 936.257 936.257v98.028h-114.92v-98.028c0-452.901-368.436-821.337-821.337-821.337H682.996l429.228 429.229-81.25 81.248-567.936-567.937L1030.975 188Zm-463.038.011 81.249 81.25-486.688 486.688 486.688 486.688-81.249 81.249L0 755.949 567.937 188.01Z'
|
||||
fill-rule='evenodd'
|
||||
fillRule='evenodd'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
@@ -12,6 +12,11 @@ vi.mock('@/lib/logs/console-logger', () => ({
|
||||
})),
|
||||
}))
|
||||
|
||||
// Blocks
|
||||
vi.mock('@/blocks/index', () => ({
|
||||
getBlock: vi.fn(),
|
||||
}))
|
||||
|
||||
// Tools
|
||||
vi.mock('@/tools/utils', () => ({
|
||||
getTool: vi.fn(),
|
||||
|
||||
@@ -1,19 +1,10 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { getBlock } from '@/blocks/index'
|
||||
import { BlockType } from '@/executor/consts'
|
||||
import { InputResolver } from '@/executor/resolver/resolver'
|
||||
import type { ExecutionContext } from '@/executor/types'
|
||||
import type { SerializedBlock, SerializedWorkflow } from '@/serializer/types'
|
||||
|
||||
// Mock logger
|
||||
vi.mock('@/lib/logs/console-logger', () => ({
|
||||
createLogger: vi.fn().mockReturnValue({
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
}),
|
||||
}))
|
||||
|
||||
describe('InputResolver', () => {
|
||||
let sampleWorkflow: SerializedWorkflow
|
||||
let mockContext: any
|
||||
@@ -1898,4 +1889,502 @@ describe('InputResolver', () => {
|
||||
expect(() => loopResolver.resolveInputs(testBlock, loopContext)).not.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Conditional Input Filtering', () => {
|
||||
const mockGetBlock = getBlock as ReturnType<typeof vi.fn>
|
||||
|
||||
afterEach(() => {
|
||||
mockGetBlock.mockReset()
|
||||
})
|
||||
|
||||
it('should filter inputs based on operation conditions for Knowledge block', () => {
|
||||
// Mock the Knowledge block configuration
|
||||
mockGetBlock.mockReturnValue({
|
||||
type: 'knowledge',
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Search', id: 'search' },
|
||||
{ label: 'Upload Chunk', id: 'upload_chunk' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'query',
|
||||
type: 'short-input',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
},
|
||||
{
|
||||
id: 'knowledgeBaseIds',
|
||||
type: 'knowledge-base-selector',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
},
|
||||
{
|
||||
id: 'documentId',
|
||||
type: 'document-selector',
|
||||
condition: { field: 'operation', value: 'upload_chunk' },
|
||||
},
|
||||
{
|
||||
id: 'content',
|
||||
type: 'long-input',
|
||||
condition: { field: 'operation', value: 'upload_chunk' },
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
// Create a Knowledge block with upload_chunk operation
|
||||
const knowledgeBlock: SerializedBlock = {
|
||||
id: 'knowledge-block',
|
||||
metadata: { id: 'knowledge', name: 'Knowledge Block' },
|
||||
position: { x: 0, y: 0 },
|
||||
config: {
|
||||
tool: 'knowledge',
|
||||
params: {
|
||||
operation: 'upload_chunk',
|
||||
query: '<start.docName>', // This should be filtered out
|
||||
knowledgeBaseIds: 'kb-1', // This should be filtered out
|
||||
documentId: 'doc-1', // This should be included
|
||||
content: 'chunk content', // This should be included
|
||||
},
|
||||
},
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
const result = resolver.resolveInputs(knowledgeBlock, mockContext)
|
||||
|
||||
// Should only include inputs for upload_chunk operation
|
||||
expect(result).toHaveProperty('operation', 'upload_chunk')
|
||||
expect(result).toHaveProperty('documentId', 'doc-1')
|
||||
expect(result).toHaveProperty('content', 'chunk content')
|
||||
|
||||
// Should NOT include inputs for search operation
|
||||
expect(result).not.toHaveProperty('query')
|
||||
expect(result).not.toHaveProperty('knowledgeBaseIds')
|
||||
})
|
||||
|
||||
it('should filter inputs based on operation conditions for Knowledge block search operation', () => {
|
||||
// Mock the Knowledge block configuration
|
||||
mockGetBlock.mockReturnValue({
|
||||
type: 'knowledge',
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Search', id: 'search' },
|
||||
{ label: 'Upload Chunk', id: 'upload_chunk' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'query',
|
||||
type: 'short-input',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
},
|
||||
{
|
||||
id: 'knowledgeBaseIds',
|
||||
type: 'knowledge-base-selector',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
},
|
||||
{
|
||||
id: 'documentId',
|
||||
type: 'document-selector',
|
||||
condition: { field: 'operation', value: 'upload_chunk' },
|
||||
},
|
||||
{
|
||||
id: 'content',
|
||||
type: 'long-input',
|
||||
condition: { field: 'operation', value: 'upload_chunk' },
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
// Create a Knowledge block with search operation
|
||||
const knowledgeBlock: SerializedBlock = {
|
||||
id: 'knowledge-block',
|
||||
metadata: { id: 'knowledge', name: 'Knowledge Block' },
|
||||
position: { x: 0, y: 0 },
|
||||
config: {
|
||||
tool: 'knowledge',
|
||||
params: {
|
||||
operation: 'search',
|
||||
query: 'search query',
|
||||
knowledgeBaseIds: 'kb-1',
|
||||
documentId: 'doc-1', // This should be filtered out
|
||||
content: 'chunk content', // This should be filtered out
|
||||
},
|
||||
},
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
const result = resolver.resolveInputs(knowledgeBlock, mockContext)
|
||||
|
||||
// Should only include inputs for search operation
|
||||
expect(result).toHaveProperty('operation', 'search')
|
||||
expect(result).toHaveProperty('query', 'search query')
|
||||
expect(result).toHaveProperty('knowledgeBaseIds', 'kb-1')
|
||||
|
||||
// Should NOT include inputs for upload_chunk operation
|
||||
expect(result).not.toHaveProperty('documentId')
|
||||
expect(result).not.toHaveProperty('content')
|
||||
})
|
||||
|
||||
it('should handle array conditions correctly', () => {
|
||||
// Mock a block with array condition
|
||||
mockGetBlock.mockReturnValue({
|
||||
type: 'test-block',
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Create', id: 'create' },
|
||||
{ label: 'Update', id: 'update' },
|
||||
{ label: 'Delete', id: 'delete' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'data',
|
||||
type: 'long-input',
|
||||
condition: { field: 'operation', value: ['create', 'update'] },
|
||||
},
|
||||
{
|
||||
id: 'id',
|
||||
type: 'short-input',
|
||||
condition: { field: 'operation', value: ['update', 'delete'] },
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const testBlock: SerializedBlock = {
|
||||
id: 'test-block',
|
||||
metadata: { id: 'test-block', name: 'Test Block' },
|
||||
position: { x: 0, y: 0 },
|
||||
config: {
|
||||
tool: 'test-block',
|
||||
params: {
|
||||
operation: 'update',
|
||||
data: 'some data',
|
||||
id: 'item-1',
|
||||
},
|
||||
},
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
const result = resolver.resolveInputs(testBlock, mockContext)
|
||||
|
||||
// Should include inputs for update operation (both data and id)
|
||||
expect(result).toHaveProperty('operation', 'update')
|
||||
expect(result).toHaveProperty('data', 'some data')
|
||||
expect(result).toHaveProperty('id', 'item-1')
|
||||
})
|
||||
|
||||
it('should include all inputs when no conditions are present', () => {
|
||||
// Mock a block with no conditions
|
||||
mockGetBlock.mockReturnValue({
|
||||
type: 'simple-block',
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'param1',
|
||||
type: 'short-input',
|
||||
},
|
||||
{
|
||||
id: 'param2',
|
||||
type: 'long-input',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const simpleBlock: SerializedBlock = {
|
||||
id: 'simple-block',
|
||||
metadata: { id: 'simple-block', name: 'Simple Block' },
|
||||
position: { x: 0, y: 0 },
|
||||
config: {
|
||||
tool: 'simple-block',
|
||||
params: {
|
||||
param1: 'value1',
|
||||
param2: 'value2',
|
||||
},
|
||||
},
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
const result = resolver.resolveInputs(simpleBlock, mockContext)
|
||||
|
||||
// Should include all inputs
|
||||
expect(result).toHaveProperty('param1', 'value1')
|
||||
expect(result).toHaveProperty('param2', 'value2')
|
||||
})
|
||||
|
||||
it('should return all inputs when block config is not found', () => {
|
||||
// Mock getBlock to return undefined
|
||||
mockGetBlock.mockReturnValue(undefined)
|
||||
|
||||
const unknownBlock: SerializedBlock = {
|
||||
id: 'unknown-block',
|
||||
metadata: { id: 'unknown-type', name: 'Unknown Block' },
|
||||
position: { x: 0, y: 0 },
|
||||
config: {
|
||||
tool: 'unknown-type',
|
||||
params: {
|
||||
param1: 'value1',
|
||||
param2: 'value2',
|
||||
},
|
||||
},
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
const result = resolver.resolveInputs(unknownBlock, mockContext)
|
||||
|
||||
// Should include all inputs when block config is not found
|
||||
expect(result).toHaveProperty('param1', 'value1')
|
||||
expect(result).toHaveProperty('param2', 'value2')
|
||||
})
|
||||
|
||||
it('should handle negated conditions correctly', () => {
|
||||
// Mock a block with negated condition
|
||||
mockGetBlock.mockReturnValue({
|
||||
type: 'test-block',
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Create', id: 'create' },
|
||||
{ label: 'Delete', id: 'delete' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'confirmationField',
|
||||
type: 'short-input',
|
||||
condition: { field: 'operation', value: 'create', not: true },
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const testBlock: SerializedBlock = {
|
||||
id: 'test-block',
|
||||
metadata: { id: 'test-block', name: 'Test Block' },
|
||||
position: { x: 0, y: 0 },
|
||||
config: {
|
||||
tool: 'test-block',
|
||||
params: {
|
||||
operation: 'delete',
|
||||
confirmationField: 'confirmed',
|
||||
},
|
||||
},
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
const result = resolver.resolveInputs(testBlock, mockContext)
|
||||
|
||||
// Should include confirmationField because operation is NOT 'create'
|
||||
expect(result).toHaveProperty('operation', 'delete')
|
||||
expect(result).toHaveProperty('confirmationField', 'confirmed')
|
||||
})
|
||||
|
||||
it('should handle compound AND conditions correctly', () => {
|
||||
// Mock a block with compound AND condition
|
||||
mockGetBlock.mockReturnValue({
|
||||
type: 'test-block',
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Create', id: 'create' },
|
||||
{ label: 'Update', id: 'update' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'enabled',
|
||||
type: 'switch',
|
||||
},
|
||||
{
|
||||
id: 'specialField',
|
||||
type: 'short-input',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'update',
|
||||
and: { field: 'enabled', value: true },
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const testBlock: SerializedBlock = {
|
||||
id: 'test-block',
|
||||
metadata: { id: 'test-block', name: 'Test Block' },
|
||||
position: { x: 0, y: 0 },
|
||||
config: {
|
||||
tool: 'test-block',
|
||||
params: {
|
||||
operation: 'update',
|
||||
enabled: true,
|
||||
specialField: 'special value',
|
||||
},
|
||||
},
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
const result = resolver.resolveInputs(testBlock, mockContext)
|
||||
|
||||
// Should include specialField because operation is 'update' AND enabled is true
|
||||
expect(result).toHaveProperty('operation', 'update')
|
||||
expect(result).toHaveProperty('enabled', true)
|
||||
expect(result).toHaveProperty('specialField', 'special value')
|
||||
})
|
||||
|
||||
it('should always include inputs without conditions', () => {
|
||||
// Mock a block with mixed conditions
|
||||
mockGetBlock.mockReturnValue({
|
||||
type: 'test-block',
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
type: 'dropdown',
|
||||
// No condition - should always be included
|
||||
},
|
||||
{
|
||||
id: 'alwaysVisible',
|
||||
type: 'short-input',
|
||||
// No condition - should always be included
|
||||
},
|
||||
{
|
||||
id: 'conditionalField',
|
||||
type: 'short-input',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const testBlock: SerializedBlock = {
|
||||
id: 'test-block',
|
||||
metadata: { id: 'test-block', name: 'Test Block' },
|
||||
position: { x: 0, y: 0 },
|
||||
config: {
|
||||
tool: 'test-block',
|
||||
params: {
|
||||
operation: 'upload',
|
||||
alwaysVisible: 'always here',
|
||||
conditionalField: 'should be filtered out',
|
||||
},
|
||||
},
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
const result = resolver.resolveInputs(testBlock, mockContext)
|
||||
|
||||
// Should include inputs without conditions
|
||||
expect(result).toHaveProperty('operation', 'upload')
|
||||
expect(result).toHaveProperty('alwaysVisible', 'always here')
|
||||
|
||||
// Should NOT include conditional field that doesn't match
|
||||
expect(result).not.toHaveProperty('conditionalField')
|
||||
})
|
||||
|
||||
it('should handle duplicate field names with different conditions (Knowledge block case)', () => {
|
||||
// Mock Knowledge block with duplicate content fields
|
||||
mockGetBlock.mockReturnValue({
|
||||
type: 'knowledge',
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
type: 'dropdown',
|
||||
},
|
||||
{
|
||||
id: 'content',
|
||||
title: 'Chunk Content',
|
||||
type: 'long-input',
|
||||
condition: { field: 'operation', value: 'upload_chunk' },
|
||||
},
|
||||
{
|
||||
id: 'content',
|
||||
title: 'Document Content',
|
||||
type: 'long-input',
|
||||
condition: { field: 'operation', value: 'create_document' },
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
// Test upload_chunk operation
|
||||
const uploadChunkBlock: SerializedBlock = {
|
||||
id: 'knowledge-block',
|
||||
metadata: { id: 'knowledge', name: 'Knowledge Block' },
|
||||
position: { x: 0, y: 0 },
|
||||
config: {
|
||||
tool: 'knowledge',
|
||||
params: {
|
||||
operation: 'upload_chunk',
|
||||
content: 'chunk content here',
|
||||
},
|
||||
},
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
const result1 = resolver.resolveInputs(uploadChunkBlock, mockContext)
|
||||
expect(result1).toHaveProperty('operation', 'upload_chunk')
|
||||
expect(result1).toHaveProperty('content', 'chunk content here')
|
||||
|
||||
// Test create_document operation
|
||||
const createDocBlock: SerializedBlock = {
|
||||
id: 'knowledge-block',
|
||||
metadata: { id: 'knowledge', name: 'Knowledge Block' },
|
||||
position: { x: 0, y: 0 },
|
||||
config: {
|
||||
tool: 'knowledge',
|
||||
params: {
|
||||
operation: 'create_document',
|
||||
content: 'document content here',
|
||||
},
|
||||
},
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
const result2 = resolver.resolveInputs(createDocBlock, mockContext)
|
||||
expect(result2).toHaveProperty('operation', 'create_document')
|
||||
expect(result2).toHaveProperty('content', 'document content here')
|
||||
|
||||
// Test search operation (should NOT include content)
|
||||
const searchBlock: SerializedBlock = {
|
||||
id: 'knowledge-block',
|
||||
metadata: { id: 'knowledge', name: 'Knowledge Block' },
|
||||
position: { x: 0, y: 0 },
|
||||
config: {
|
||||
tool: 'knowledge',
|
||||
params: {
|
||||
operation: 'search',
|
||||
content: 'should be filtered out',
|
||||
},
|
||||
},
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
const result3 = resolver.resolveInputs(searchBlock, mockContext)
|
||||
expect(result3).toHaveProperty('operation', 'search')
|
||||
expect(result3).not.toHaveProperty('content')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { BlockPathCalculator } from '@/lib/block-path-calculator'
|
||||
import { createLogger } from '@/lib/logs/console-logger'
|
||||
import { VariableManager } from '@/lib/variables/variable-manager'
|
||||
import { getBlock } from '@/blocks/index'
|
||||
import type { LoopManager } from '@/executor/loops/loops'
|
||||
import type { ExecutionContext } from '@/executor/types'
|
||||
import type { SerializedBlock, SerializedWorkflow } from '@/serializer/types'
|
||||
@@ -55,6 +56,102 @@ export class InputResolver {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates if a sub-block should be active based on its condition
|
||||
* @param condition - The condition to evaluate
|
||||
* @param currentValues - Current values of all inputs
|
||||
* @returns True if the sub-block should be active
|
||||
*/
|
||||
private evaluateSubBlockCondition(
|
||||
condition:
|
||||
| {
|
||||
field: string
|
||||
value: any
|
||||
not?: boolean
|
||||
and?: { field: string; value: any; not?: boolean }
|
||||
}
|
||||
| undefined,
|
||||
currentValues: Record<string, any>
|
||||
): boolean {
|
||||
if (!condition) return true
|
||||
|
||||
// Get the field value
|
||||
const fieldValue = currentValues[condition.field]
|
||||
|
||||
// Check if the condition value is an array
|
||||
const isValueMatch = Array.isArray(condition.value)
|
||||
? fieldValue != null &&
|
||||
(condition.not
|
||||
? !condition.value.includes(fieldValue)
|
||||
: condition.value.includes(fieldValue))
|
||||
: condition.not
|
||||
? fieldValue !== condition.value
|
||||
: fieldValue === condition.value
|
||||
|
||||
// Check both conditions if 'and' is present
|
||||
const isAndValueMatch =
|
||||
!condition.and ||
|
||||
(() => {
|
||||
const andFieldValue = currentValues[condition.and!.field]
|
||||
return Array.isArray(condition.and!.value)
|
||||
? andFieldValue != null &&
|
||||
(condition.and!.not
|
||||
? !condition.and!.value.includes(andFieldValue)
|
||||
: condition.and!.value.includes(andFieldValue))
|
||||
: condition.and!.not
|
||||
? andFieldValue !== condition.and!.value
|
||||
: andFieldValue === condition.and!.value
|
||||
})()
|
||||
|
||||
return isValueMatch && isAndValueMatch
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters inputs based on sub-block conditions
|
||||
* @param block - Block to filter inputs for
|
||||
* @param inputs - All input parameters
|
||||
* @returns Filtered input parameters that should be processed
|
||||
*/
|
||||
private filterInputsByConditions(
|
||||
block: SerializedBlock,
|
||||
inputs: Record<string, any>
|
||||
): Record<string, any> {
|
||||
const blockType = block.metadata?.id
|
||||
if (!blockType) return inputs
|
||||
|
||||
const blockConfig = getBlock(blockType)
|
||||
if (!blockConfig || !blockConfig.subBlocks) return inputs
|
||||
|
||||
// Filter inputs based on conditions
|
||||
const filteredInputs: Record<string, any> = {}
|
||||
for (const [key, value] of Object.entries(inputs)) {
|
||||
// Check if this input should be included based on subBlock conditions
|
||||
let shouldInclude = false
|
||||
|
||||
// Find all subBlocks with this ID
|
||||
const matchingSubBlocks = blockConfig.subBlocks.filter((sb) => sb.id === key)
|
||||
|
||||
if (matchingSubBlocks.length === 0) {
|
||||
// No subBlock config found for this input - include it
|
||||
shouldInclude = true
|
||||
} else {
|
||||
// Check if any of the matching subBlocks should be active
|
||||
for (const subBlock of matchingSubBlocks) {
|
||||
if (!subBlock.condition || this.evaluateSubBlockCondition(subBlock.condition, inputs)) {
|
||||
shouldInclude = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldInclude) {
|
||||
filteredInputs[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return filteredInputs
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves all inputs for a block based on current context.
|
||||
* Handles block references, environment variables, and JSON parsing.
|
||||
@@ -64,7 +161,9 @@ export class InputResolver {
|
||||
* @returns Resolved input parameters
|
||||
*/
|
||||
resolveInputs(block: SerializedBlock, context: ExecutionContext): Record<string, any> {
|
||||
const inputs = { ...block.config.params }
|
||||
const allInputs = { ...block.config.params }
|
||||
// Filter inputs based on sub-block conditions to only process active fields
|
||||
const inputs = this.filterInputsByConditions(block, allInputs)
|
||||
const result: Record<string, any> = {}
|
||||
// Process each input parameter
|
||||
for (const [key, value] of Object.entries(inputs)) {
|
||||
|
||||
@@ -87,3 +87,10 @@ export interface AirtableUpdateMultipleResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type AirtableResponse =
|
||||
| AirtableListResponse
|
||||
| AirtableGetResponse
|
||||
| AirtableCreateResponse
|
||||
| AirtableUpdateResponse
|
||||
| AirtableUpdateMultipleResponse
|
||||
|
||||
@@ -27,3 +27,12 @@ export interface BrowserUseTaskOutput {
|
||||
export interface BrowserUseRunTaskResponse extends ToolResponse {
|
||||
output: BrowserUseTaskOutput
|
||||
}
|
||||
|
||||
export interface BrowserUseResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
success: boolean
|
||||
output: any
|
||||
steps: BrowserUseTaskStep[]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,3 +42,5 @@ export interface ConfluenceUpdateResponse extends ToolResponse {
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export type ConfluenceResponse = ConfluenceRetrieveResponse | ConfluenceUpdateResponse
|
||||
|
||||
@@ -12,3 +12,9 @@ export interface ElevenLabsTtsResponse extends ToolResponse {
|
||||
audioUrl: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface ElevenLabsBlockResponse extends ToolResponse {
|
||||
output: {
|
||||
audioUrl: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,3 +88,9 @@ export interface ExaAnswerResponse extends ToolResponse {
|
||||
}[]
|
||||
}
|
||||
}
|
||||
|
||||
export type ExaResponse =
|
||||
| ExaSearchResponse
|
||||
| ExaGetContentsResponse
|
||||
| ExaFindSimilarLinksResponse
|
||||
| ExaAnswerResponse
|
||||
|
||||
@@ -58,3 +58,5 @@ export interface SearchResponse extends ToolResponse {
|
||||
warning?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type FirecrawlResponse = ScrapeResponse | SearchResponse
|
||||
|
||||
@@ -147,3 +147,9 @@ export interface RepoInfoResponse extends ToolResponse {
|
||||
metadata: RepoMetadata
|
||||
}
|
||||
}
|
||||
|
||||
export type GitHubResponse =
|
||||
| PullRequestResponse
|
||||
| CreateCommentResponse
|
||||
| LatestCommitResponse
|
||||
| RepoInfoResponse
|
||||
|
||||
@@ -276,3 +276,11 @@ export interface GoogleCalendarApiListResponse {
|
||||
nextSyncToken?: string
|
||||
items: GoogleCalendarApiEventResponse[]
|
||||
}
|
||||
|
||||
export type GoogleCalendarResponse =
|
||||
| GoogleCalendarCreateResponse
|
||||
| GoogleCalendarListResponse
|
||||
| GoogleCalendarGetResponse
|
||||
| GoogleCalendarQuickAddResponse
|
||||
| GoogleCalendarInviteResponse
|
||||
| GoogleCalendarUpdateResponse
|
||||
|
||||
@@ -38,3 +38,8 @@ export interface GoogleDocsToolParams {
|
||||
folderId?: string
|
||||
folderSelector?: string
|
||||
}
|
||||
|
||||
export type GoogleDocsResponse =
|
||||
| GoogleDocsReadResponse
|
||||
| GoogleDocsWriteResponse
|
||||
| GoogleDocsCreateResponse
|
||||
|
||||
@@ -45,3 +45,8 @@ export interface GoogleDriveToolParams {
|
||||
pageToken?: string
|
||||
exportMimeType?: string
|
||||
}
|
||||
|
||||
export type GoogleDriveResponse =
|
||||
| GoogleDriveUploadResponse
|
||||
| GoogleDriveGetContentResponse
|
||||
| GoogleDriveListResponse
|
||||
|
||||
@@ -69,3 +69,9 @@ export interface GoogleSheetsToolParams {
|
||||
responseValueRenderOption?: 'FORMATTED_VALUE' | 'UNFORMATTED_VALUE' | 'FORMULA'
|
||||
majorDimension?: 'ROWS' | 'COLUMNS'
|
||||
}
|
||||
|
||||
export type GoogleSheetsResponse =
|
||||
| GoogleSheetsReadResponse
|
||||
| GoogleSheetsWriteResponse
|
||||
| GoogleSheetsUpdateResponse
|
||||
| GoogleSheetsAppendResponse
|
||||
|
||||
@@ -104,3 +104,9 @@ export interface JiraCloudResource {
|
||||
scopes: string[]
|
||||
avatarUrl: string
|
||||
}
|
||||
|
||||
export type JiraResponse =
|
||||
| JiraRetrieveResponse
|
||||
| JiraUpdateResponse
|
||||
| JiraWriteResponse
|
||||
| JiraRetrieveResponseBulk
|
||||
|
||||
@@ -34,3 +34,5 @@ export interface LinearCreateIssueResponse extends ToolResponse {
|
||||
issue: LinearIssue
|
||||
}
|
||||
}
|
||||
|
||||
export type LinearResponse = LinearReadIssuesResponse | LinearCreateIssueResponse
|
||||
|
||||
@@ -67,3 +67,8 @@ export interface MicrosoftExcelTableToolParams {
|
||||
values: ExcelCellValue[][]
|
||||
rowIndex?: number
|
||||
}
|
||||
|
||||
export type MicrosoftExcelResponse =
|
||||
| MicrosoftExcelReadResponse
|
||||
| MicrosoftExcelWriteResponse
|
||||
| MicrosoftExcelTableAddResponse
|
||||
|
||||
@@ -57,3 +57,5 @@ export interface MicrosoftTeamsToolParams {
|
||||
teamId?: string
|
||||
content?: string
|
||||
}
|
||||
|
||||
export type MicrosoftTeamsResponse = MicrosoftTeamsReadResponse | MicrosoftTeamsWriteResponse
|
||||
|
||||
@@ -127,3 +127,5 @@ export interface CleanedOutlookMessage {
|
||||
isRead?: boolean
|
||||
importance?: string
|
||||
}
|
||||
|
||||
export type OutlookResponse = OutlookReadResponse | OutlookSendResponse | OutlookDraftResponse
|
||||
|
||||
@@ -74,3 +74,5 @@ export interface RedditCommentsResponse extends ToolResponse {
|
||||
comments: RedditComment[]
|
||||
}
|
||||
}
|
||||
|
||||
export type RedditResponse = RedditHotPostsResponse | RedditPostsResponse | RedditCommentsResponse
|
||||
|
||||
@@ -18,3 +18,5 @@ export interface SlackMessageResponse extends ToolResponse {
|
||||
channel: string
|
||||
}
|
||||
}
|
||||
|
||||
export type SlackResponse = SlackMessageResponse
|
||||
|
||||
@@ -13,18 +13,16 @@ export interface SupabaseInsertParams {
|
||||
data: any
|
||||
}
|
||||
|
||||
export interface SupabaseQueryResponse extends ToolResponse {
|
||||
error?: string
|
||||
export interface SupabaseBaseResponse extends ToolResponse {
|
||||
output: {
|
||||
message: string
|
||||
results: any
|
||||
}
|
||||
error?: string
|
||||
}
|
||||
|
||||
export interface SupabaseInsertResponse extends ToolResponse {
|
||||
error?: string
|
||||
output: {
|
||||
message: string
|
||||
results: any
|
||||
}
|
||||
}
|
||||
export interface SupabaseQueryResponse extends SupabaseBaseResponse {}
|
||||
|
||||
export interface SupabaseInsertResponse extends SupabaseBaseResponse {}
|
||||
|
||||
export interface SupabaseResponse extends SupabaseBaseResponse {}
|
||||
|
||||
@@ -70,3 +70,5 @@ export interface SearchResponse extends ToolResponse {
|
||||
response_time: number
|
||||
}
|
||||
}
|
||||
|
||||
export type TavilyResponse = TavilySearchResponse | TavilyExtractResponse
|
||||
|
||||
@@ -103,3 +103,10 @@ export interface TypeformResponsesResponse extends ToolResponse {
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
export interface TypeformResponse extends ToolResponse {
|
||||
output:
|
||||
| TypeformResponsesResponse['output']
|
||||
| TypeformFilesResponse['output']
|
||||
| TypeformInsightsData
|
||||
}
|
||||
|
||||
@@ -140,3 +140,5 @@ export interface WealthboxWriteParams {
|
||||
category?: number
|
||||
priority?: 'Low' | 'Medium' | 'High'
|
||||
}
|
||||
|
||||
export type WealthboxResponse = WealthboxReadResponse | WealthboxWriteResponse
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { createLogger } from '@/lib/logs/console-logger'
|
||||
import type { ToolConfig } from '../types'
|
||||
import type { WhatsAppToolResponse } from './types'
|
||||
import type { WhatsAppResponse, WhatsAppSendMessageParams } from './types'
|
||||
|
||||
const logger = createLogger('WhatsAppSendMessageTool')
|
||||
|
||||
export const sendMessageTool: ToolConfig<any, WhatsAppToolResponse> = {
|
||||
export const sendMessageTool: ToolConfig<WhatsAppSendMessageParams, WhatsAppResponse> = {
|
||||
id: 'whatsapp_send_message',
|
||||
name: 'WhatsApp',
|
||||
description: 'Send WhatsApp messages',
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import type { ToolResponse } from '../types'
|
||||
|
||||
export interface WhatsAppToolResponse extends ToolResponse {
|
||||
export interface WhatsAppSendMessageParams {
|
||||
phoneNumber: string
|
||||
message: string
|
||||
phoneNumberId: string
|
||||
accessToken: string
|
||||
}
|
||||
|
||||
export interface WhatsAppResponse extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
messageId?: string
|
||||
|
||||
@@ -105,3 +105,5 @@ export interface XUserResponse extends ToolResponse {
|
||||
recentTweets?: XTweet[]
|
||||
}
|
||||
}
|
||||
|
||||
export type XResponse = XWriteResponse | XReadResponse | XSearchResponse | XUserResponse
|
||||
|
||||
Reference in New Issue
Block a user