diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 0393dbf9f..4d0d8df90 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -301,8 +301,8 @@ In addition, you will need to update the registries: ```typescript:/apps/sim/blocks/blocks/pinecone.ts import { PineconeIcon } from '@/components/icons' - import { PineconeResponse } from '@/tools/pinecone/types' - import { BlockConfig } from '../types' + import type { BlockConfig } from '@/blocks/types' + import type { PineconeResponse } from '@/tools/pinecone/types' export const PineconeBlock: BlockConfig = { type: 'pinecone', @@ -313,13 +313,58 @@ In addition, you will need to update the registries: bgColor: '#123456', icon: PineconeIcon, - // If this block requires OAuth authentication - provider: 'pinecone', - - // Define subBlocks for the UI configuration subBlocks: [ - // Block configuration options + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + layout: 'full', + required: true, + options: [ + { label: 'Generate Embeddings', id: 'generate' }, + { label: 'Search Text', id: 'search_text' }, + ], + value: () => 'generate', + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Your Pinecone API key', + password: true, + required: true, + }, ], + + tools: { + access: ['pinecone_generate_embeddings', 'pinecone_search_text'], + config: { + tool: (params: Record) => { + switch (params.operation) { + case 'generate': + return 'pinecone_generate_embeddings' + case 'search_text': + return 'pinecone_search_text' + default: + throw new Error('Invalid operation selected') + } + }, + }, + }, + + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + apiKey: { type: 'string', description: 'Pinecone API key' }, + searchQuery: { type: 'string', description: 'Search query text' }, + topK: { type: 'string', description: 'Number of results to return' }, + }, + + outputs: { + matches: { type: 'any', description: 'Search results or generated embeddings' }, + data: { type: 'any', description: 'Response data from Pinecone' }, + usage: { type: 'any', description: 'API usage statistics' }, + }, } ``` diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b32f61ffa..3592883c5 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,42 +1,25 @@ -## Description +## Summary +Brief description of what this PR does and why. -Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. +Fixes #(issue) -Fixes # (issue) +## Type of Change +- [ ] Bug fix +- [ ] New feature +- [ ] Breaking change +- [ ] Documentation +- [ ] Other: ___________ -## Type of change +## Testing +How has this been tested? What should reviewers focus on? -Please delete options that are not relevant. - -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] Documentation update -- [ ] Security enhancement -- [ ] Performance improvement -- [ ] Code refactoring (no functional changes) - -## How Has This Been Tested? - -Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. - -## Checklist: - -- [ ] My code follows the style guidelines of this project -- [ ] I have performed a self-review of my own code -- [ ] I have commented my code, particularly in hard-to-understand areas -- [ ] I have added tests that prove my fix is effective or that my feature works -- [ ] All tests pass locally and in CI (`bun run test`) -- [ ] My changes generate no new warnings -- [ ] Any dependent changes have been merged and published in downstream modules -- [ ] I have updated version numbers as needed (if needed) +## Checklist +- [ ] Code follows project style guidelines +- [ ] Self-reviewed my changes +- [ ] Tests added/updated and passing +- [ ] No new warnings introduced - [ ] I confirm that I have read and agree to the terms outlined in the [Contributor License Agreement (CLA)](./CONTRIBUTING.md#contributor-license-agreement-cla) -## Security Considerations: - -- [ ] My changes do not introduce any new security vulnerabilities -- [ ] I have considered the security implications of my changes - -## Additional Information: - -Any additional information, configuration or data that might be necessary to reproduce the issue or use the feature. +## Screenshots/Videos + + diff --git a/apps/docs/content/docs/tools/airtable.mdx b/apps/docs/content/docs/tools/airtable.mdx index 9fb44da30..a078806f0 100644 --- a/apps/docs/content/docs/tools/airtable.mdx +++ b/apps/docs/content/docs/tools/airtable.mdx @@ -79,11 +79,11 @@ Read records from an Airtable table #### Output -| Parameter | Type | -| --------- | ---- | -| `records` | string | -| `metadata` | string | -| `totalRecords` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `records` | json | Retrieved record data | +| `record` | json | Single record data | +| `metadata` | json | Operation metadata | ### `airtable_get_record` @@ -100,10 +100,11 @@ Retrieve a single record from an Airtable table by its ID #### Output -| Parameter | Type | -| --------- | ---- | -| `record` | string | -| `metadata` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `records` | json | Retrieved record data | +| `record` | json | Single record data | +| `metadata` | json | Operation metadata | ### `airtable_create_records` @@ -119,10 +120,11 @@ Write new records to an Airtable table #### Output -| Parameter | Type | -| --------- | ---- | -| `records` | string | -| `metadata` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `records` | json | Retrieved record data | +| `record` | json | Single record data | +| `metadata` | json | Operation metadata | ### `airtable_update_record` @@ -140,11 +142,11 @@ Update an existing record in an Airtable table by ID #### Output -| Parameter | Type | -| --------- | ---- | -| `record` | string | -| `metadata` | string | -| `updatedFields` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `records` | json | Retrieved record data | +| `record` | json | Single record data | +| `metadata` | json | Operation metadata | ### `airtable_update_multiple_records` @@ -160,33 +162,14 @@ Update multiple existing records in an Airtable table #### Output -| Parameter | Type | -| --------- | ---- | -| `records` | string | -| `metadata` | string | -| `updatedRecordIds` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `records` | json | Retrieved record data | +| `record` | json | Single record data | +| `metadata` | json | Operation metadata | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `records` | json | records output from the block | -| `record` | json | record output from the block | -| `metadata` | json | metadata output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/arxiv.mdx b/apps/docs/content/docs/tools/arxiv.mdx index b6d14c260..b18140b41 100644 --- a/apps/docs/content/docs/tools/arxiv.mdx +++ b/apps/docs/content/docs/tools/arxiv.mdx @@ -61,7 +61,7 @@ Search for academic papers on ArXiv by keywords, authors, titles, or other field | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `query` | string | Yes | The search query to execute | +| `searchQuery` | string | Yes | The search query to execute | | `searchField` | string | No | Field to search in: all, ti \(title\), au \(author\), abs \(abstract\), co \(comment\), jr \(journal\), cat \(category\), rn \(report number\) | | `maxResults` | number | No | Maximum number of results to return \(default: 10, max: 2000\) | | `sortBy` | string | No | Sort by: relevance, lastUpdatedDate, submittedDate \(default: relevance\) | @@ -69,11 +69,12 @@ Search for academic papers on ArXiv by keywords, authors, titles, or other field #### Output -| Parameter | Type | -| --------- | ---- | -| `query` | string | -| `papers` | string | -| `totalResults` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `papers` | json | Found papers data | +| `totalResults` | number | Total results count | +| `paper` | json | Paper details | +| `authorPapers` | json | Author papers list | ### `arxiv_get_paper` @@ -87,9 +88,12 @@ Get detailed information about a specific ArXiv paper by its ID. #### Output -| Parameter | Type | -| --------- | ---- | -| `paper` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `papers` | json | Found papers data | +| `totalResults` | number | Total results count | +| `paper` | json | Paper details | +| `authorPapers` | json | Author papers list | ### `arxiv_get_author_papers` @@ -104,34 +108,15 @@ Search for papers by a specific author on ArXiv. #### Output -| Parameter | Type | -| --------- | ---- | -| `authorPapers` | string | -| `authorName` | string | -| `totalResults` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `papers` | json | Found papers data | +| `totalResults` | number | Total results count | +| `paper` | json | Paper details | +| `authorPapers` | json | Author papers list | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `papers` | json | papers output from the block | -| `totalResults` | number | totalResults output from the block | -| `paper` | json | paper output from the block | -| `authorPapers` | json | authorPapers output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/browser_use.mdx b/apps/docs/content/docs/tools/browser_use.mdx index 2d91c0d2e..2063625a6 100644 --- a/apps/docs/content/docs/tools/browser_use.mdx +++ b/apps/docs/content/docs/tools/browser_use.mdx @@ -79,35 +79,15 @@ Runs a browser automation task using BrowserUse #### Output -| Parameter | Type | -| --------- | ---- | -| `id` | string | -| `success` | string | -| `output` | string | -| `steps` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Task execution identifier | +| `success` | boolean | Task completion status | +| `output` | any | Task output data | +| `steps` | json | Execution steps taken | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `task` | string | Yes | Task - Describe what the browser agent should do... | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `id` | string | id output from the block | -| `success` | boolean | success output from the block | -| `output` | any | output output from the block | -| `steps` | json | steps output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/clay.mdx b/apps/docs/content/docs/tools/clay.mdx index 4e83223b7..6320eabb0 100644 --- a/apps/docs/content/docs/tools/clay.mdx +++ b/apps/docs/content/docs/tools/clay.mdx @@ -218,29 +218,12 @@ Populate Clay with data from a JSON file. Enables direct communication and notif #### Output -| Parameter | Type | -| --------- | ---- | -| `data` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `data` | any | Response data | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `authToken` | string | Yes | Auth Token - Enter your Clay Auth token | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `data` | any | data output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/confluence.mdx b/apps/docs/content/docs/tools/confluence.mdx index d69b157c4..311f3d7e2 100644 --- a/apps/docs/content/docs/tools/confluence.mdx +++ b/apps/docs/content/docs/tools/confluence.mdx @@ -64,12 +64,13 @@ Retrieve content from Confluence pages using the Confluence API. #### Output -| Parameter | Type | -| --------- | ---- | -| `ts` | string | -| `pageId` | string | -| `content` | string | -| `title` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ts` | string | Timestamp | +| `pageId` | string | Page identifier | +| `content` | string | Page content | +| `title` | string | Page title | +| `success` | boolean | Operation success status | ### `confluence_update` @@ -89,37 +90,16 @@ Update a Confluence page using the Confluence API. #### Output -| Parameter | Type | -| --------- | ---- | -| `ts` | string | -| `pageId` | string | -| `title` | string | -| `body` | string | -| `success` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ts` | string | Timestamp | +| `pageId` | string | Page identifier | +| `content` | string | Page content | +| `title` | string | Page title | +| `success` | boolean | Operation success status | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `ts` | string | ts output from the block | -| `pageId` | string | pageId output from the block | -| `content` | string | content output from the block | -| `title` | string | title output from the block | -| `success` | boolean | success output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/discord.mdx b/apps/docs/content/docs/tools/discord.mdx index bfde04381..674e834e3 100644 --- a/apps/docs/content/docs/tools/discord.mdx +++ b/apps/docs/content/docs/tools/discord.mdx @@ -78,9 +78,10 @@ Send a message to a Discord channel #### Output -| Parameter | Type | -| --------- | ---- | -| `message` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `message` | string | Message content | +| `data` | any | Response data | ### `discord_get_messages` @@ -96,9 +97,10 @@ Retrieve messages from a Discord channel #### Output -| Parameter | Type | -| --------- | ---- | -| `message` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `message` | string | Message content | +| `data` | any | Response data | ### `discord_get_server` @@ -113,9 +115,10 @@ Retrieve information about a Discord server (guild) #### Output -| Parameter | Type | -| --------- | ---- | -| `message` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `message` | string | Message content | +| `data` | any | Response data | ### `discord_get_user` @@ -130,30 +133,13 @@ Retrieve information about a Discord user #### Output -| Parameter | Type | -| --------- | ---- | -| `message` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `message` | string | Message content | +| `data` | any | Response data | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `message` | string | message output from the block | -| `data` | any | data output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/elevenlabs.mdx b/apps/docs/content/docs/tools/elevenlabs.mdx index 11d77ca19..2e0d6a770 100644 --- a/apps/docs/content/docs/tools/elevenlabs.mdx +++ b/apps/docs/content/docs/tools/elevenlabs.mdx @@ -60,29 +60,12 @@ Convert TTS using ElevenLabs voices #### Output -| Parameter | Type | -| --------- | ---- | -| `audioUrl` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `audioUrl` | string | Generated audio URL | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `text` | string | Yes | Text - Enter the text to convert to speech | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `audioUrl` | string | audioUrl output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/exa.mdx b/apps/docs/content/docs/tools/exa.mdx index 88acd2f9e..34e860ff2 100644 --- a/apps/docs/content/docs/tools/exa.mdx +++ b/apps/docs/content/docs/tools/exa.mdx @@ -61,22 +61,18 @@ Search the web using Exa AI. Returns relevant search results with titles, URLs, | `query` | string | Yes | The search query to execute | | `numResults` | number | No | Number of results to return \(default: 10, max: 25\) | | `useAutoprompt` | boolean | No | Whether to use autoprompt to improve the query \(default: false\) | -| `type` | string | No | Search type: neural, keyword, auto or magic \(default: auto\) | +| `type` | string | No | Search type: neural, keyword, auto or fast \(default: auto\) | | `apiKey` | string | Yes | Exa AI API Key | #### Output -| Parameter | Type | -| --------- | ---- | -| `results` | string | -| `url` | string | -| `publishedDate` | string | -| `author` | string | -| `summary` | string | -| `favicon` | string | -| `image` | string | -| `text` | string | -| `score` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results | +| `similarLinks` | json | Similar links found | +| `answer` | string | Generated answer | +| `citations` | json | Answer citations | +| `research` | json | Research findings | ### `exa_get_contents` @@ -93,12 +89,13 @@ Retrieve the contents of webpages using Exa AI. Returns the title, text content, #### Output -| Parameter | Type | -| --------- | ---- | -| `results` | string | -| `title` | string | -| `text` | string | -| `summary` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results | +| `similarLinks` | json | Similar links found | +| `answer` | string | Generated answer | +| `citations` | json | Answer citations | +| `research` | json | Research findings | ### `exa_find_similar_links` @@ -115,12 +112,13 @@ Find webpages similar to a given URL using Exa AI. Returns a list of similar lin #### Output -| Parameter | Type | -| --------- | ---- | -| `similarLinks` | string | -| `url` | string | -| `text` | string | -| `score` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results | +| `similarLinks` | json | Similar links found | +| `answer` | string | Generated answer | +| `citations` | json | Answer citations | +| `research` | json | Research findings | ### `exa_answer` @@ -136,13 +134,13 @@ Get an AI-generated answer to a question with citations from the web using Exa A #### Output -| Parameter | Type | -| --------- | ---- | -| `query` | string | -| `answer` | string | -| `citations` | string | -| `url` | string | -| `text` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results | +| `similarLinks` | json | Similar links found | +| `answer` | string | Generated answer | +| `citations` | json | Answer citations | +| `research` | json | Research findings | ### `exa_research` @@ -158,34 +156,16 @@ Perform comprehensive research using AI to generate detailed reports with citati #### Output -| Parameter | Type | -| --------- | ---- | -| `taskId` | string | -| `research` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results | +| `similarLinks` | json | Similar links found | +| `answer` | string | Generated answer | +| `citations` | json | Answer citations | +| `research` | json | Research findings | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `results` | json | results output from the block | -| `similarLinks` | json | similarLinks output from the block | -| `answer` | string | answer output from the block | -| `citations` | json | citations output from the block | -| `research` | json | research output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/file.mdx b/apps/docs/content/docs/tools/file.mdx index 398bb1bf2..f7afb49c4 100644 --- a/apps/docs/content/docs/tools/file.mdx +++ b/apps/docs/content/docs/tools/file.mdx @@ -69,28 +69,13 @@ Parse one or more uploaded files or files from URLs (text, PDF, CSV, images, etc #### Output -This tool does not produce any outputs. +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `files` | json | Parsed file data | +| `combinedContent` | string | Combined file content | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `inputMethod` | string | No | | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `files` | json | files output from the block | -| `combinedContent` | string | combinedContent output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/firecrawl.mdx b/apps/docs/content/docs/tools/firecrawl.mdx index ec90e7ccc..ba28ff956 100644 --- a/apps/docs/content/docs/tools/firecrawl.mdx +++ b/apps/docs/content/docs/tools/firecrawl.mdx @@ -79,11 +79,16 @@ Extract structured content from web pages with comprehensive metadata support. C #### Output -| Parameter | Type | -| --------- | ---- | -| `markdown` | string | -| `html` | string | -| `metadata` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `markdown` | string | Page content markdown | +| `html` | any | Raw HTML content | +| `metadata` | json | Page metadata | +| `data` | json | Search results data | +| `warning` | any | Warning messages | +| `pages` | json | Crawled pages data | +| `total` | number | Total pages found | +| `creditsUsed` | number | Credits consumed | ### `firecrawl_search` @@ -98,10 +103,16 @@ Search for information on the web using Firecrawl #### Output -| Parameter | Type | -| --------- | ---- | -| `data` | string | -| `warning` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `markdown` | string | Page content markdown | +| `html` | any | Raw HTML content | +| `metadata` | json | Page metadata | +| `data` | json | Search results data | +| `warning` | any | Warning messages | +| `pages` | json | Crawled pages data | +| `total` | number | Total pages found | +| `creditsUsed` | number | Credits consumed | ### `firecrawl_crawl` @@ -118,39 +129,19 @@ Crawl entire websites and extract structured content from all accessible pages #### Output -| Parameter | Type | -| --------- | ---- | -| `jobId` | string | -| `pages` | string | -| `total` | string | -| `creditsUsed` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `markdown` | string | Page content markdown | +| `html` | any | Raw HTML content | +| `metadata` | json | Page metadata | +| `data` | json | Search results data | +| `warning` | any | Warning messages | +| `pages` | json | Crawled pages data | +| `total` | number | Total pages found | +| `creditsUsed` | number | Credits consumed | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `markdown` | string | markdown output from the block | -| `html` | any | html output from the block | -| `metadata` | json | metadata output from the block | -| `data` | json | data output from the block | -| `warning` | any | warning output from the block | -| `pages` | json | pages output from the block | -| `total` | number | total output from the block | -| `creditsUsed` | number | creditsUsed output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/github.mdx b/apps/docs/content/docs/tools/github.mdx index e0298fbb5..b9b48839a 100644 --- a/apps/docs/content/docs/tools/github.mdx +++ b/apps/docs/content/docs/tools/github.mdx @@ -56,24 +56,10 @@ Fetch PR details including diff and files changed #### Output -| Parameter | Type | -| --------- | ---- | -| `metadata` | string | -| `title` | string | -| `state` | string | -| `html_url` | string | -| `diff_url` | string | -| `created_at` | string | -| `updated_at` | string | -| `files` | string | -| `additions` | string | -| `deletions` | string | -| `changes` | string | -| `patch` | string | -| `blob_url` | string | -| `raw_url` | string | -| `status` | string | -| `content` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Response content | +| `metadata` | json | Response metadata | ### `github_comment` @@ -97,17 +83,10 @@ Create comments on GitHub PRs #### Output -| Parameter | Type | -| --------- | ---- | -| `metadata` | string | -| `html_url` | string | -| `created_at` | string | -| `updated_at` | string | -| `path` | string | -| `line` | string | -| `side` | string | -| `commit_id` | string | -| `content` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Response content | +| `metadata` | json | Response metadata | ### `github_repo_info` @@ -123,15 +102,10 @@ Retrieve comprehensive GitHub repository metadata including stars, forks, issues #### Output -| Parameter | Type | -| --------- | ---- | -| `metadata` | string | -| `description` | string | -| `stars` | string | -| `forks` | string | -| `openIssues` | string | -| `language` | string | -| `content` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Response content | +| `metadata` | json | Response metadata | ### `github_latest_commit` @@ -148,36 +122,13 @@ Retrieve the latest commit from a GitHub repository #### Output -| Parameter | Type | -| --------- | ---- | -| `metadata` | string | -| `html_url` | string | -| `commit_message` | string | -| `author` | string | -| `login` | string | -| `avatar_url` | string | -| `content` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Response content | +| `metadata` | json | Response metadata | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `content` | string | content output from the block | -| `metadata` | json | metadata output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/gmail.mdx b/apps/docs/content/docs/tools/gmail.mdx index 1e3335f84..a12d6cc67 100644 --- a/apps/docs/content/docs/tools/gmail.mdx +++ b/apps/docs/content/docs/tools/gmail.mdx @@ -72,12 +72,10 @@ Send emails using Gmail #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | -| `metadata` | string | -| `threadId` | string | -| `labelIds` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Response content | +| `metadata` | json | Email metadata | ### `gmail_draft` @@ -94,13 +92,10 @@ Draft emails using Gmail #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | -| `metadata` | string | -| `message` | string | -| `threadId` | string | -| `labelIds` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Response content | +| `metadata` | json | Email metadata | ### `gmail_read` @@ -118,10 +113,10 @@ Read emails from Gmail #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | -| `metadata` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Response content | +| `metadata` | json | Email metadata | ### `gmail_search` @@ -137,31 +132,13 @@ Search emails in Gmail #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | -| `metadata` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Response content | +| `metadata` | json | Email metadata | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `content` | string | content output from the block | -| `metadata` | json | metadata output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/google_calendar.mdx b/apps/docs/content/docs/tools/google_calendar.mdx index b54309ec6..eb0269562 100644 --- a/apps/docs/content/docs/tools/google_calendar.mdx +++ b/apps/docs/content/docs/tools/google_calendar.mdx @@ -117,9 +117,10 @@ Create a new event in Google Calendar #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Operation response content | +| `metadata` | json | Event metadata | ### `google_calendar_list` @@ -138,9 +139,10 @@ List events from Google Calendar #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Operation response content | +| `metadata` | json | Event metadata | ### `google_calendar_get` @@ -156,9 +158,10 @@ Get a specific event from Google Calendar #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Operation response content | +| `metadata` | json | Event metadata | ### `google_calendar_quick_add` @@ -176,9 +179,10 @@ Create events from natural language text #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Operation response content | +| `metadata` | json | Event metadata | ### `google_calendar_invite` @@ -197,41 +201,13 @@ Invite attendees to an existing Google Calendar event #### Output -| Parameter | Type | -| --------- | ---- | -| `metadata` | string | -| `htmlLink` | string | -| `status` | string | -| `summary` | string | -| `description` | string | -| `location` | string | -| `start` | string | -| `end` | string | -| `attendees` | string | -| `creator` | string | -| `organizer` | string | -| `content` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Operation response content | +| `metadata` | json | Event metadata | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `content` | string | content output from the block | -| `metadata` | json | metadata output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/google_docs.mdx b/apps/docs/content/docs/tools/google_docs.mdx index 274bf5c3c..cfac1a14d 100644 --- a/apps/docs/content/docs/tools/google_docs.mdx +++ b/apps/docs/content/docs/tools/google_docs.mdx @@ -100,10 +100,11 @@ Read content from a Google Docs document #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | -| `metadata` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Document content | +| `metadata` | json | Document metadata | +| `updatedContent` | boolean | Content update status | ### `google_docs_write` @@ -119,10 +120,11 @@ Write or update content in a Google Docs document #### Output -| Parameter | Type | -| --------- | ---- | -| `updatedContent` | string | -| `metadata` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Document content | +| `metadata` | json | Document metadata | +| `updatedContent` | boolean | Content update status | ### `google_docs_create` @@ -140,31 +142,14 @@ Create a new Google Docs document #### Output -| Parameter | Type | -| --------- | ---- | -| `metadata` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Document content | +| `metadata` | json | Document metadata | +| `updatedContent` | boolean | Content update status | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `content` | string | content output from the block | -| `metadata` | json | metadata output from the block | -| `updatedContent` | boolean | updatedContent output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/google_drive.mdx b/apps/docs/content/docs/tools/google_drive.mdx index 2ba25bfd2..d729ff6d5 100644 --- a/apps/docs/content/docs/tools/google_drive.mdx +++ b/apps/docs/content/docs/tools/google_drive.mdx @@ -96,17 +96,10 @@ Upload a file to Google Drive #### Output -| Parameter | Type | -| --------- | ---- | -| `file` | string | -| `name` | string | -| `mimeType` | string | -| `webViewLink` | string | -| `webContentLink` | string | -| `size` | string | -| `createdTime` | string | -| `modifiedTime` | string | -| `parents` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `file` | json | File data | +| `files` | json | Files list | ### `google_drive_create_folder` @@ -123,17 +116,10 @@ Create a new folder in Google Drive #### Output -| Parameter | Type | -| --------- | ---- | -| `file` | string | -| `name` | string | -| `mimeType` | string | -| `webViewLink` | string | -| `webContentLink` | string | -| `size` | string | -| `createdTime` | string | -| `modifiedTime` | string | -| `parents` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `file` | json | File data | +| `files` | json | Files list | ### `google_drive_list` @@ -152,38 +138,13 @@ List files and folders in Google Drive #### Output -| Parameter | Type | -| --------- | ---- | -| `files` | string | -| `name` | string | -| `mimeType` | string | -| `webViewLink` | string | -| `webContentLink` | string | -| `size` | string | -| `createdTime` | string | -| `modifiedTime` | string | -| `parents` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `file` | json | File data | +| `files` | json | Files list | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `file` | json | file output from the block | -| `files` | json | files output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/google_search.mdx b/apps/docs/content/docs/tools/google_search.mdx index 34ed4a2a6..b98863031 100644 --- a/apps/docs/content/docs/tools/google_search.mdx +++ b/apps/docs/content/docs/tools/google_search.mdx @@ -79,34 +79,13 @@ Search the web with the Custom Search API #### Output -| Parameter | Type | -| --------- | ---- | -| `items` | string | -| `searchInformation` | string | -| `searchTime` | string | -| `formattedSearchTime` | string | -| `formattedTotalResults` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `items` | json | Search result items | +| `searchInformation` | json | Search metadata | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `query` | string | Yes | Search Query - Enter your search query | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `items` | json | items output from the block | -| `searchInformation` | json | searchInformation output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/google_sheets.mdx b/apps/docs/content/docs/tools/google_sheets.mdx index 353438664..00909a360 100644 --- a/apps/docs/content/docs/tools/google_sheets.mdx +++ b/apps/docs/content/docs/tools/google_sheets.mdx @@ -116,9 +116,15 @@ Read data from a Google Sheets spreadsheet #### Output -| Parameter | Type | -| --------- | ---- | -| `data` | json | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `data` | json | Sheet data | +| `metadata` | json | Operation metadata | +| `updatedRange` | string | Updated range | +| `updatedRows` | number | Updated rows count | +| `updatedColumns` | number | Updated columns count | +| `updatedCells` | number | Updated cells count | +| `tableRange` | string | Table range | ### `google_sheets_write` @@ -137,15 +143,15 @@ Write data to a Google Sheets spreadsheet #### Output -| Parameter | Type | -| --------- | ---- | -| `updatedRange` | string | -| `updatedRows` | string | -| `updatedColumns` | string | -| `updatedCells` | string | -| `metadata` | string | -| `spreadsheetId` | string | -| `spreadsheetUrl` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `data` | json | Sheet data | +| `metadata` | json | Operation metadata | +| `updatedRange` | string | Updated range | +| `updatedRows` | number | Updated rows count | +| `updatedColumns` | number | Updated columns count | +| `updatedCells` | number | Updated cells count | +| `tableRange` | string | Table range | ### `google_sheets_update` @@ -164,15 +170,15 @@ Update data in a Google Sheets spreadsheet #### Output -| Parameter | Type | -| --------- | ---- | -| `updatedRange` | string | -| `updatedRows` | string | -| `updatedColumns` | string | -| `updatedCells` | string | -| `metadata` | string | -| `spreadsheetId` | string | -| `spreadsheetUrl` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `data` | json | Sheet data | +| `metadata` | json | Operation metadata | +| `updatedRange` | string | Updated range | +| `updatedRows` | number | Updated rows count | +| `updatedColumns` | number | Updated columns count | +| `updatedCells` | number | Updated cells count | +| `tableRange` | string | Table range | ### `google_sheets_append` @@ -192,35 +198,18 @@ Append data to the end of a Google Sheets spreadsheet #### Output -| Parameter | Type | -| --------- | ---- | -| `data` | json | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `data` | json | Sheet data | +| `metadata` | json | Operation metadata | +| `updatedRange` | string | Updated range | +| `updatedRows` | number | Updated rows count | +| `updatedColumns` | number | Updated columns count | +| `updatedCells` | number | Updated cells count | +| `tableRange` | string | Table range | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `data` | json | data output from the block | -| `metadata` | json | metadata output from the block | -| `updatedRange` | string | updatedRange output from the block | -| `updatedRows` | number | updatedRows output from the block | -| `updatedColumns` | number | updatedColumns output from the block | -| `updatedCells` | number | updatedCells output from the block | -| `tableRange` | string | tableRange output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/huggingface.mdx b/apps/docs/content/docs/tools/huggingface.mdx index f01d65f10..6e0875a4e 100644 --- a/apps/docs/content/docs/tools/huggingface.mdx +++ b/apps/docs/content/docs/tools/huggingface.mdx @@ -90,35 +90,14 @@ Generate completions using Hugging Face Inference API #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | -| `model` | string | -| `usage` | string | -| `completion_tokens` | string | -| `total_tokens` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Generated response | +| `model` | string | Model used | +| `usage` | json | Token usage stats | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `systemPrompt` | string | No | System Prompt - Enter system prompt to guide the model behavior... | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `content` | string | content output from the block | -| `model` | string | model output from the block | -| `usage` | json | usage output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/hunter.mdx b/apps/docs/content/docs/tools/hunter.mdx new file mode 100644 index 000000000..abab69c5c --- /dev/null +++ b/apps/docs/content/docs/tools/hunter.mdx @@ -0,0 +1,204 @@ +--- +title: Hunter io +description: Find and verify professional email addresses +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + + `} +/> + +## Usage Instructions + +Search for email addresses, verify their deliverability, discover companies, and enrich contact data using Hunter.io's powerful email finding capabilities. + + + +## Tools + +### `hunter_discover` + +Returns companies matching a set of criteria using Hunter.io AI-powered search. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `query` | string | No | Natural language search query for companies | +| `domain` | string | No | Company domain names to filter by | +| `headcount` | string | No | Company size filter \(e.g., | +| `company_type` | string | No | Type of organization | +| `technology` | string | No | Technology used by companies | +| `apiKey` | string | Yes | Hunter.io API Key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results | +| `emails` | json | Email addresses found | +| `email` | string | Found email address | +| `score` | number | Confidence score | +| `result` | string | Verification result | +| `status` | string | Status message | +| `total` | number | Total results count | +| `personal_emails` | number | Personal emails count | +| `generic_emails` | number | Generic emails count | + +### `hunter_domain_search` + +Returns all the email addresses found using one given domain name, with sources. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `domain` | string | Yes | Domain name to search for email addresses | +| `limit` | number | No | Maximum email addresses to return \(default: 10\) | +| `offset` | number | No | Number of email addresses to skip | +| `type` | string | No | Filter for personal or generic emails | +| `seniority` | string | No | Filter by seniority level: junior, senior, or executive | +| `department` | string | No | Filter by specific departments \(e.g., sales, marketing\) | +| `apiKey` | string | Yes | Hunter.io API Key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results | +| `emails` | json | Email addresses found | +| `email` | string | Found email address | +| `score` | number | Confidence score | +| `result` | string | Verification result | +| `status` | string | Status message | +| `total` | number | Total results count | +| `personal_emails` | number | Personal emails count | +| `generic_emails` | number | Generic emails count | + +### `hunter_email_finder` + +Finds the most likely email address for a person given their name and company domain. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `domain` | string | Yes | Company domain name | +| `first_name` | string | Yes | Person | +| `last_name` | string | Yes | Person | +| `company` | string | No | Company name | +| `apiKey` | string | Yes | Hunter.io API Key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results | +| `emails` | json | Email addresses found | +| `email` | string | Found email address | +| `score` | number | Confidence score | +| `result` | string | Verification result | +| `status` | string | Status message | +| `total` | number | Total results count | +| `personal_emails` | number | Personal emails count | +| `generic_emails` | number | Generic emails count | + +### `hunter_email_verifier` + +Verifies the deliverability of an email address and provides detailed verification status. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | The email address to verify | +| `apiKey` | string | Yes | Hunter.io API Key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results | +| `emails` | json | Email addresses found | +| `email` | string | Found email address | +| `score` | number | Confidence score | +| `result` | string | Verification result | +| `status` | string | Status message | +| `total` | number | Total results count | +| `personal_emails` | number | Personal emails count | +| `generic_emails` | number | Generic emails count | + +### `hunter_companies_find` + +Enriches company data using domain name. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `domain` | string | Yes | Domain to find company data for | +| `apiKey` | string | Yes | Hunter.io API Key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results | +| `emails` | json | Email addresses found | +| `email` | string | Found email address | +| `score` | number | Confidence score | +| `result` | string | Verification result | +| `status` | string | Status message | +| `total` | number | Total results count | +| `personal_emails` | number | Personal emails count | +| `generic_emails` | number | Generic emails count | + +### `hunter_email_count` + +Returns the total number of email addresses found for a domain or company. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `domain` | string | No | Domain to count emails for \(required if company not provided\) | +| `company` | string | No | Company name to count emails for \(required if domain not provided\) | +| `type` | string | No | Filter for personal or generic emails only | +| `apiKey` | string | Yes | Hunter.io API Key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results | +| `emails` | json | Email addresses found | +| `email` | string | Found email address | +| `score` | number | Confidence score | +| `result` | string | Verification result | +| `status` | string | Status message | +| `total` | number | Total results count | +| `personal_emails` | number | Personal emails count | +| `generic_emails` | number | Generic emails count | + + + +## Notes + +- Category: `tools` +- Type: `hunter` diff --git a/apps/docs/content/docs/tools/image_generator.mdx b/apps/docs/content/docs/tools/image_generator.mdx index 5e005969c..2614231f4 100644 --- a/apps/docs/content/docs/tools/image_generator.mdx +++ b/apps/docs/content/docs/tools/image_generator.mdx @@ -71,33 +71,14 @@ Generate images using OpenAI #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | -| `image` | string | -| `metadata` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Generation response | +| `image` | string | Generated image URL | +| `metadata` | json | Generation metadata | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `prompt` | string | Yes | | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `content` | string | content output from the block | -| `image` | string | image output from the block | -| `metadata` | json | metadata output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/jina.mdx b/apps/docs/content/docs/tools/jina.mdx index d3921c0e2..7daef77f5 100644 --- a/apps/docs/content/docs/tools/jina.mdx +++ b/apps/docs/content/docs/tools/jina.mdx @@ -85,29 +85,12 @@ Extract and process web content into clean, LLM-friendly text using Jina AI Read #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Extracted content | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `url` | string | Yes | URL - Enter URL to extract content from | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `content` | string | content output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/jira.mdx b/apps/docs/content/docs/tools/jira.mdx index 3e750a03f..19973d373 100644 --- a/apps/docs/content/docs/tools/jira.mdx +++ b/apps/docs/content/docs/tools/jira.mdx @@ -65,14 +65,16 @@ Retrieve detailed information about a specific Jira issue #### Output -| Parameter | Type | -| --------- | ---- | -| `ts` | string | -| `issueKey` | string | -| `summary` | string | -| `description` | string | -| `created` | string | -| `updated` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ts` | string | Timestamp | +| `issueKey` | string | Issue key | +| `summary` | string | Issue summary | +| `description` | string | Issue description | +| `created` | string | Creation date | +| `updated` | string | Update date | +| `success` | boolean | Operation success | +| `url` | string | Issue URL | ### `jira_update` @@ -95,12 +97,16 @@ Update a Jira issue #### Output -| Parameter | Type | -| --------- | ---- | -| `ts` | string | -| `issueKey` | string | -| `summary` | string | -| `success` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ts` | string | Timestamp | +| `issueKey` | string | Issue key | +| `summary` | string | Issue summary | +| `description` | string | Issue description | +| `created` | string | Creation date | +| `updated` | string | Update date | +| `success` | boolean | Operation success | +| `url` | string | Issue URL | ### `jira_write` @@ -118,17 +124,20 @@ Write a Jira issue | `priority` | string | No | Priority for the issue | | `assignee` | string | No | Assignee for the issue | | `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. | -| `issueType` | string | Yes | Type of issue to create \(e.g., Task, Story, Bug, Sub-task\) | +| `issueType` | string | Yes | Type of issue to create \(e.g., Task, Story\) | #### Output -| Parameter | Type | -| --------- | ---- | -| `ts` | string | -| `issueKey` | string | -| `summary` | string | -| `success` | string | -| `url` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ts` | string | Timestamp | +| `issueKey` | string | Issue key | +| `summary` | string | Issue summary | +| `description` | string | Issue description | +| `created` | string | Creation date | +| `updated` | string | Update date | +| `success` | boolean | Operation success | +| `url` | string | Issue URL | ### `jira_bulk_read` @@ -145,36 +154,19 @@ Retrieve multiple Jira issues in bulk #### Output -| Parameter | Type | -| --------- | ---- | -| `issues` | array | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ts` | string | Timestamp | +| `issueKey` | string | Issue key | +| `summary` | string | Issue summary | +| `description` | string | Issue description | +| `created` | string | Creation date | +| `updated` | string | Update date | +| `success` | boolean | Operation success | +| `url` | string | Issue URL | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `ts` | string | ts output from the block | -| `issueKey` | string | issueKey output from the block | -| `summary` | string | summary output from the block | -| `description` | string | description output from the block | -| `created` | string | created output from the block | -| `updated` | string | updated output from the block | -| `success` | boolean | success output from the block | -| `url` | string | url output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/knowledge.mdx b/apps/docs/content/docs/tools/knowledge.mdx index ea9f20f1a..b42ce28fb 100644 --- a/apps/docs/content/docs/tools/knowledge.mdx +++ b/apps/docs/content/docs/tools/knowledge.mdx @@ -57,31 +57,24 @@ Perform semantic vector search across knowledge bases, upload individual chunks ### `knowledge_search` -Search for similar content in one or more knowledge bases using vector similarity +Search for similar content in a knowledge base using vector similarity #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `knowledgeBaseIds` | string | Yes | ID of the knowledge base to search in, or comma-separated IDs for multiple knowledge bases | +| `knowledgeBaseId` | string | Yes | ID of the knowledge base to search in | | `query` | string | Yes | Search query text | | `topK` | number | No | Number of most similar results to return \(1-100\) | -| `tag1` | string | No | Filter by tag 1 value | -| `tag2` | string | No | Filter by tag 2 value | -| `tag3` | string | No | Filter by tag 3 value | -| `tag4` | string | No | Filter by tag 4 value | -| `tag5` | string | No | Filter by tag 5 value | -| `tag6` | string | No | Filter by tag 6 value | -| `tag7` | string | No | Filter by tag 7 value | +| `tagFilters` | any | No | Array of tag filters with tagName and tagValue properties | #### Output -| Parameter | Type | -| --------- | ---- | -| `results` | string | -| `query` | string | -| `totalResults` | string | -| `cost` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results | +| `query` | string | Query used | +| `totalResults` | number | Total results count | ### `knowledge_upload_chunk` @@ -97,16 +90,11 @@ Upload a new chunk to a document in a knowledge base #### Output -| Parameter | Type | -| --------- | ---- | -| `data` | string | -| `chunkIndex` | string | -| `content` | string | -| `contentLength` | string | -| `tokenCount` | string | -| `enabled` | string | -| `createdAt` | string | -| `updatedAt` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results | +| `query` | string | Query used | +| `totalResults` | number | Total results count | ### `knowledge_create_document` @@ -126,35 +114,18 @@ Create a new document in a knowledge base | `tag5` | string | No | Tag 5 value for the document | | `tag6` | string | No | Tag 6 value for the document | | `tag7` | string | No | Tag 7 value for the document | +| `documentTagsData` | array | No | Structured tag data with names, types, and values | #### Output -| Parameter | Type | -| --------- | ---- | -| `data` | string | -| `name` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results | +| `query` | string | Query used | +| `totalResults` | number | Total results count | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `results` | json | results output from the block | -| `query` | string | query output from the block | -| `totalResults` | number | totalResults output from the block | - - ## Notes - Category: `blocks` diff --git a/apps/docs/content/docs/tools/linear.mdx b/apps/docs/content/docs/tools/linear.mdx index 4374a9638..a541add5a 100644 --- a/apps/docs/content/docs/tools/linear.mdx +++ b/apps/docs/content/docs/tools/linear.mdx @@ -61,9 +61,10 @@ Fetch and filter issues from Linear #### Output -| Parameter | Type | -| --------- | ---- | -| `issues` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `issues` | json | Issues list | +| `issue` | json | Single issue data | ### `linear_create_issue` @@ -80,35 +81,13 @@ Create a new issue in Linear #### Output -| Parameter | Type | -| --------- | ---- | -| `issue` | string | -| `title` | string | -| `description` | string | -| `state` | string | -| `teamId` | string | -| `projectId` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `issues` | json | Issues list | +| `issue` | json | Single issue data | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `issues` | json | issues output from the block | -| `issue` | json | issue output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/linkup.mdx b/apps/docs/content/docs/tools/linkup.mdx index 5c0e4b6de..24c15bb1a 100644 --- a/apps/docs/content/docs/tools/linkup.mdx +++ b/apps/docs/content/docs/tools/linkup.mdx @@ -64,31 +64,13 @@ Search the web for information using Linkup #### Output -| Parameter | Type | -| --------- | ---- | -| `answer` | string | -| `sources` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `answer` | string | Generated answer | +| `sources` | json | Source references | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `q` | string | Yes | Search Query - Enter your search query | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `answer` | string | answer output from the block | -| `sources` | json | sources output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/mem0.mdx b/apps/docs/content/docs/tools/mem0.mdx index ac060f716..6c9448d05 100644 --- a/apps/docs/content/docs/tools/mem0.mdx +++ b/apps/docs/content/docs/tools/mem0.mdx @@ -64,9 +64,11 @@ Add memories to Mem0 for persistent storage and retrieval #### Output -| Parameter | Type | -| --------- | ---- | -| `memories` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ids` | any | Memory identifiers | +| `memories` | any | Memory data | +| `searchResults` | any | Search results | ### `mem0_search_memories` @@ -83,10 +85,11 @@ Search for memories in Mem0 using semantic search #### Output -| Parameter | Type | -| --------- | ---- | -| `searchResults` | string | -| `ids` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ids` | any | Memory identifiers | +| `memories` | any | Memory data | +| `searchResults` | any | Search results | ### `mem0_get_memories` @@ -105,32 +108,14 @@ Retrieve memories from Mem0 by ID or filter criteria #### Output -| Parameter | Type | -| --------- | ---- | -| `memories` | string | -| `ids` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ids` | any | Memory identifiers | +| `memories` | any | Memory data | +| `searchResults` | any | Search results | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `ids` | any | ids output from the block | -| `memories` | any | memories output from the block | -| `searchResults` | any | searchResults output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/memory.mdx b/apps/docs/content/docs/tools/memory.mdx index 1a236dba5..fcd46da35 100644 --- a/apps/docs/content/docs/tools/memory.mdx +++ b/apps/docs/content/docs/tools/memory.mdx @@ -55,9 +55,10 @@ Add a new memory to the database or append to existing memory with the same ID. #### Output -| Parameter | Type | -| --------- | ---- | -| `memories` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `memories` | any | Memory data | +| `id` | string | Memory identifier | ### `memory_get` @@ -71,10 +72,10 @@ Retrieve a specific memory by its ID #### Output -| Parameter | Type | -| --------- | ---- | -| `memories` | string | -| `message` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `memories` | any | Memory data | +| `id` | string | Memory identifier | ### `memory_get_all` @@ -87,10 +88,10 @@ Retrieve all memories from the database #### Output -| Parameter | Type | -| --------- | ---- | -| `message` | string | -| `memories` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `memories` | any | Memory data | +| `id` | string | Memory identifier | ### `memory_delete` @@ -104,30 +105,13 @@ Delete a specific memory by its ID #### Output -| Parameter | Type | -| --------- | ---- | -| `message` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `memories` | any | Memory data | +| `id` | string | Memory identifier | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `memories` | any | memories output from the block | -| `id` | string | id output from the block | - - ## Notes - Category: `blocks` diff --git a/apps/docs/content/docs/tools/meta.json b/apps/docs/content/docs/tools/meta.json index a047b4a7b..db0fc6c07 100644 --- a/apps/docs/content/docs/tools/meta.json +++ b/apps/docs/content/docs/tools/meta.json @@ -19,6 +19,7 @@ "google_search", "google_sheets", "huggingface", + "hunter", "image_generator", "jina", "jira", diff --git a/apps/docs/content/docs/tools/microsoft_excel.mdx b/apps/docs/content/docs/tools/microsoft_excel.mdx index b915b262c..15ecd8e28 100644 --- a/apps/docs/content/docs/tools/microsoft_excel.mdx +++ b/apps/docs/content/docs/tools/microsoft_excel.mdx @@ -114,9 +114,16 @@ Read data from a Microsoft Excel spreadsheet #### Output -| Parameter | Type | -| --------- | ---- | -| `data` | json | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `data` | json | Sheet data | +| `metadata` | json | Operation metadata | +| `updatedRange` | string | Updated range | +| `updatedRows` | number | Updated rows count | +| `updatedColumns` | number | Updated columns count | +| `updatedCells` | number | Updated cells count | +| `index` | number | Row index | +| `values` | json | Table values | ### `microsoft_excel_write` @@ -135,15 +142,16 @@ Write data to a Microsoft Excel spreadsheet #### Output -| Parameter | Type | -| --------- | ---- | -| `updatedRange` | string | -| `updatedRows` | string | -| `updatedColumns` | string | -| `updatedCells` | string | -| `metadata` | string | -| `spreadsheetId` | string | -| `spreadsheetUrl` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `data` | json | Sheet data | +| `metadata` | json | Operation metadata | +| `updatedRange` | string | Updated range | +| `updatedRows` | number | Updated rows count | +| `updatedColumns` | number | Updated columns count | +| `updatedCells` | number | Updated cells count | +| `index` | number | Row index | +| `values` | json | Table values | ### `microsoft_excel_table_add` @@ -160,36 +168,19 @@ Add new rows to a Microsoft Excel table #### Output -| Parameter | Type | -| --------- | ---- | -| `data` | json | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `data` | json | Sheet data | +| `metadata` | json | Operation metadata | +| `updatedRange` | string | Updated range | +| `updatedRows` | number | Updated rows count | +| `updatedColumns` | number | Updated columns count | +| `updatedCells` | number | Updated cells count | +| `index` | number | Row index | +| `values` | json | Table values | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `data` | json | data output from the block | -| `metadata` | json | metadata output from the block | -| `updatedRange` | string | updatedRange output from the block | -| `updatedRows` | number | updatedRows output from the block | -| `updatedColumns` | number | updatedColumns output from the block | -| `updatedCells` | number | updatedCells output from the block | -| `index` | number | index output from the block | -| `values` | json | values output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/microsoft_teams.mdx b/apps/docs/content/docs/tools/microsoft_teams.mdx index a1ae20df5..51e80b011 100644 --- a/apps/docs/content/docs/tools/microsoft_teams.mdx +++ b/apps/docs/content/docs/tools/microsoft_teams.mdx @@ -117,14 +117,11 @@ Read content from a Microsoft Teams chat #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | -| `metadata` | string | -| `messageCount` | string | -| `messages` | string | -| `totalAttachments` | string | -| `attachmentTypes` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Message content | +| `metadata` | json | Message metadata | +| `updatedContent` | boolean | Content update status | ### `microsoft_teams_write_chat` @@ -140,10 +137,11 @@ Write or update content in a Microsoft Teams chat #### Output -| Parameter | Type | -| --------- | ---- | -| `updatedContent` | string | -| `metadata` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Message content | +| `metadata` | json | Message metadata | +| `updatedContent` | boolean | Content update status | ### `microsoft_teams_read_channel` @@ -159,15 +157,11 @@ Read content from a Microsoft Teams channel #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | -| `metadata` | string | -| `channelId` | string | -| `messageCount` | string | -| `messages` | string | -| `totalAttachments` | string | -| `attachmentTypes` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Message content | +| `metadata` | json | Message metadata | +| `updatedContent` | boolean | Content update status | ### `microsoft_teams_write_channel` @@ -184,32 +178,14 @@ Write or send a message to a Microsoft Teams channel #### Output -| Parameter | Type | -| --------- | ---- | -| `updatedContent` | string | -| `metadata` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Message content | +| `metadata` | json | Message metadata | +| `updatedContent` | boolean | Content update status | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `content` | string | content output from the block | -| `metadata` | json | metadata output from the block | -| `updatedContent` | boolean | updatedContent output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/mistral_parse.mdx b/apps/docs/content/docs/tools/mistral_parse.mdx index b26d96ca8..eb4700322 100644 --- a/apps/docs/content/docs/tools/mistral_parse.mdx +++ b/apps/docs/content/docs/tools/mistral_parse.mdx @@ -104,28 +104,13 @@ Parse PDF documents using Mistral OCR API #### Output -This tool does not produce any outputs. +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Extracted content | +| `metadata` | json | Processing metadata | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `inputMethod` | string | No | | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `content` | string | content output from the block | -| `metadata` | json | metadata output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/notion.mdx b/apps/docs/content/docs/tools/notion.mdx index 6a48f5f49..51801ce37 100644 --- a/apps/docs/content/docs/tools/notion.mdx +++ b/apps/docs/content/docs/tools/notion.mdx @@ -64,13 +64,10 @@ Read content from a Notion page #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | -| `metadata` | string | -| `lastEditedTime` | string | -| `createdTime` | string | -| `url` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Page content | +| `metadata` | any | Page metadata | ### `notion_read_database` @@ -85,16 +82,10 @@ Read database information and structure from Notion #### Output -| Parameter | Type | -| --------- | ---- | -| `metadata` | string | -| `url` | string | -| `id` | string | -| `createdTime` | string | -| `lastEditedTime` | string | -| `properties` | string | -| `content` | string | -| `title` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Page content | +| `metadata` | any | Page metadata | ### `notion_write` @@ -110,9 +101,10 @@ Append content to a Notion page #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Page content | +| `metadata` | any | Page metadata | ### `notion_create_page` @@ -129,9 +121,10 @@ Create a new page in Notion #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Page content | +| `metadata` | any | Page metadata | ### `notion_query_database` @@ -149,13 +142,10 @@ Query and filter Notion database entries with advanced filtering #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | -| `metadata` | string | -| `hasMore` | string | -| `nextCursor` | string | -| `results` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Page content | +| `metadata` | any | Page metadata | ### `notion_search` @@ -172,13 +162,10 @@ Search across all pages and databases in Notion workspace #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | -| `metadata` | string | -| `hasMore` | string | -| `nextCursor` | string | -| `results` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Page content | +| `metadata` | any | Page metadata | ### `notion_create_database` @@ -195,35 +182,13 @@ Create a new database in Notion with custom properties #### Output -| Parameter | Type | -| --------- | ---- | -| `metadata` | string | -| `url` | string | -| `createdTime` | string | -| `properties` | string | -| `content` | string | -| `title` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Page content | +| `metadata` | any | Page metadata | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `content` | string | content output from the block | -| `metadata` | any | metadata output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/openai.mdx b/apps/docs/content/docs/tools/openai.mdx index 647cdeecf..b8d04338b 100644 --- a/apps/docs/content/docs/tools/openai.mdx +++ b/apps/docs/content/docs/tools/openai.mdx @@ -64,34 +64,14 @@ Generate embeddings from text using OpenAI #### Output -| Parameter | Type | -| --------- | ---- | -| `embeddings` | string | -| `model` | string | -| `usage` | string | -| `total_tokens` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `embeddings` | json | Generated embeddings | +| `model` | string | Model used | +| `usage` | json | Token usage | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `input` | string | Yes | Input Text - Enter text to generate embeddings for | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `embeddings` | json | embeddings output from the block | -| `model` | string | model output from the block | -| `usage` | json | usage output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/outlook.mdx b/apps/docs/content/docs/tools/outlook.mdx index f4382d148..6e1873b64 100644 --- a/apps/docs/content/docs/tools/outlook.mdx +++ b/apps/docs/content/docs/tools/outlook.mdx @@ -161,11 +161,10 @@ Send emails using Outlook #### Output -| Parameter | Type | -| --------- | ---- | -| `message` | string | -| `results` | string | -| `timestamp` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `message` | string | Response message | +| `results` | json | Email results | ### `outlook_draft` @@ -182,13 +181,10 @@ Draft emails using Outlook #### Output -| Parameter | Type | -| --------- | ---- | -| `message` | string | -| `results` | string | -| `subject` | string | -| `status` | string | -| `timestamp` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `message` | string | Response message | +| `results` | json | Email results | ### `outlook_read` @@ -204,31 +200,13 @@ Read emails from Outlook #### Output -| Parameter | Type | -| --------- | ---- | -| `message` | string | -| `results` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `message` | string | Response message | +| `results` | json | Email results | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `message` | string | message output from the block | -| `results` | json | results output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/perplexity.mdx b/apps/docs/content/docs/tools/perplexity.mdx index 797f45f72..b8855fdde 100644 --- a/apps/docs/content/docs/tools/perplexity.mdx +++ b/apps/docs/content/docs/tools/perplexity.mdx @@ -60,35 +60,14 @@ Generate completions using Perplexity AI chat models #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | -| `model` | string | -| `usage` | string | -| `completion_tokens` | string | -| `total_tokens` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Generated response | +| `model` | string | Model used | +| `usage` | json | Token usage | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `content` | string | Yes | User Prompt - Enter your prompt here... | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `content` | string | content output from the block | -| `model` | string | model output from the block | -| `usage` | json | usage output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/pinecone.mdx b/apps/docs/content/docs/tools/pinecone.mdx index 58bcb2bfb..76baccb1a 100644 --- a/apps/docs/content/docs/tools/pinecone.mdx +++ b/apps/docs/content/docs/tools/pinecone.mdx @@ -65,12 +65,14 @@ Generate embeddings from text using Pinecone #### Output -| Parameter | Type | -| --------- | ---- | -| `data` | string | -| `model` | string | -| `vector_type` | string | -| `usage` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `matches` | any | Search matches | +| `upsertedCount` | any | Upserted count | +| `data` | any | Response data | +| `model` | any | Model information | +| `vector_type` | any | Vector type | +| `usage` | any | Usage statistics | ### `pinecone_upsert_text` @@ -87,9 +89,14 @@ Insert or update text records in a Pinecone index #### Output -| Parameter | Type | -| --------- | ---- | -| `statusText` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `matches` | any | Search matches | +| `upsertedCount` | any | Upserted count | +| `data` | any | Response data | +| `model` | any | Model information | +| `vector_type` | any | Vector type | +| `usage` | any | Usage statistics | ### `pinecone_search_text` @@ -110,11 +117,14 @@ Search for similar text in a Pinecone index #### Output -| Parameter | Type | -| --------- | ---- | -| `matches` | string | -| `score` | string | -| `metadata` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `matches` | any | Search matches | +| `upsertedCount` | any | Upserted count | +| `data` | any | Response data | +| `model` | any | Model information | +| `vector_type` | any | Vector type | +| `usage` | any | Usage statistics | ### `pinecone_search_vector` @@ -135,12 +145,14 @@ Search for similar vectors in a Pinecone index #### Output -| Parameter | Type | -| --------- | ---- | -| `matches` | string | -| `score` | string | -| `values` | string | -| `metadata` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `matches` | any | Search matches | +| `upsertedCount` | any | Upserted count | +| `data` | any | Response data | +| `model` | any | Model information | +| `vector_type` | any | Vector type | +| `usage` | any | Usage statistics | ### `pinecone_fetch` @@ -157,38 +169,17 @@ Fetch vectors by ID from a Pinecone index #### Output -| Parameter | Type | -| --------- | ---- | -| `matches` | string | -| `values` | string | -| `metadata` | string | -| `score` | string | -| `id` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `matches` | any | Search matches | +| `upsertedCount` | any | Upserted count | +| `data` | any | Response data | +| `model` | any | Model information | +| `vector_type` | any | Vector type | +| `usage` | any | Usage statistics | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `matches` | any | matches output from the block | -| `upsertedCount` | any | upsertedCount output from the block | -| `data` | any | data output from the block | -| `model` | any | model output from the block | -| `vector_type` | any | vector_type output from the block | -| `usage` | any | usage output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/qdrant.mdx b/apps/docs/content/docs/tools/qdrant.mdx index 73d9f0dee..fcc2c478b 100644 --- a/apps/docs/content/docs/tools/qdrant.mdx +++ b/apps/docs/content/docs/tools/qdrant.mdx @@ -124,10 +124,12 @@ Insert or update points in a Qdrant collection #### Output -| Parameter | Type | -| --------- | ---- | -| `status` | string | -| `data` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `matches` | any | Search matches | +| `upsertedCount` | any | Upserted count | +| `data` | any | Response data | +| `status` | any | Operation status | ### `qdrant_search_vector` @@ -148,10 +150,12 @@ Search for similar vectors in a Qdrant collection #### Output -| Parameter | Type | -| --------- | ---- | -| `data` | string | -| `status` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `matches` | any | Search matches | +| `upsertedCount` | any | Upserted count | +| `data` | any | Response data | +| `status` | any | Operation status | ### `qdrant_fetch_points` @@ -170,33 +174,15 @@ Fetch points by ID from a Qdrant collection #### Output -| Parameter | Type | -| --------- | ---- | -| `data` | string | -| `status` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `matches` | any | Search matches | +| `upsertedCount` | any | Upserted count | +| `data` | any | Response data | +| `status` | any | Operation status | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `matches` | any | matches output from the block | -| `upsertedCount` | any | upsertedCount output from the block | -| `data` | any | data output from the block | -| `status` | any | status output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/reddit.mdx b/apps/docs/content/docs/tools/reddit.mdx index ef80acf0e..22d16546a 100644 --- a/apps/docs/content/docs/tools/reddit.mdx +++ b/apps/docs/content/docs/tools/reddit.mdx @@ -61,10 +61,12 @@ Fetch posts from a subreddit with different sorting options #### Output -| Parameter | Type | -| --------- | ---- | -| `subreddit` | string | -| `posts` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `subreddit` | string | Subreddit name | +| `posts` | json | Posts data | +| `post` | json | Single post data | +| `comments` | json | Comments data | ### `reddit_get_comments` @@ -82,38 +84,15 @@ Fetch comments from a specific Reddit post #### Output -| Parameter | Type | -| --------- | ---- | -| `post` | string | -| `title` | string | -| `author` | string | -| `selftext` | string | -| `created_utc` | string | -| `score` | string | -| `permalink` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `subreddit` | string | Subreddit name | +| `posts` | json | Posts data | +| `post` | json | Single post data | +| `comments` | json | Comments data | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `subreddit` | string | subreddit output from the block | -| `posts` | json | posts output from the block | -| `post` | json | post output from the block | -| `comments` | json | comments output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/s3.mdx b/apps/docs/content/docs/tools/s3.mdx index ade38b20d..f8d62e2f7 100644 --- a/apps/docs/content/docs/tools/s3.mdx +++ b/apps/docs/content/docs/tools/s3.mdx @@ -78,38 +78,17 @@ Retrieve an object from an AWS S3 bucket | --------- | ---- | -------- | ----------- | | `accessKeyId` | string | Yes | Your AWS Access Key ID | | `secretAccessKey` | string | Yes | Your AWS Secret Access Key | -| `s3Uri` | string | Yes | S3 Object URL \(e.g., https://bucket-name.s3.region.amazonaws.com/path/to/file\) | +| `s3Uri` | string | Yes | S3 Object URL | #### Output -| Parameter | Type | -| --------- | ---- | -| `metadata` | string | -| `size` | string | -| `name` | string | -| `lastModified` | string | -| `url` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `url` | string | Presigned URL | +| `metadata` | json | Object metadata | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `accessKeyId` | string | Yes | Access Key ID - Enter your AWS Access Key ID | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `url` | string | url output from the block | -| `metadata` | json | metadata output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/schedule.mdx b/apps/docs/content/docs/tools/schedule.mdx index 30964438f..5332f9002 100644 --- a/apps/docs/content/docs/tools/schedule.mdx +++ b/apps/docs/content/docs/tools/schedule.mdx @@ -36,21 +36,6 @@ Configure automated workflow execution with flexible timing options. Set up recu -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `scheduleConfig` | schedule-config | Yes | Schedule Status | -| `scheduleType` | dropdown | Yes | Frequency | - - - -### Outputs - -This block does not produce any outputs. - ## Notes - Category: `triggers` diff --git a/apps/docs/content/docs/tools/serper.mdx b/apps/docs/content/docs/tools/serper.mdx index 65c056326..e04ce7b3a 100644 --- a/apps/docs/content/docs/tools/serper.mdx +++ b/apps/docs/content/docs/tools/serper.mdx @@ -101,29 +101,12 @@ A powerful web search tool that provides access to Google search results through #### Output -| Parameter | Type | -| --------- | ---- | -| `searchResults` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `searchResults` | json | Search results data | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `query` | string | Yes | Search Query - Enter your search query... | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `searchResults` | json | searchResults output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/slack.mdx b/apps/docs/content/docs/tools/slack.mdx index d16b88893..074e9c1d1 100644 --- a/apps/docs/content/docs/tools/slack.mdx +++ b/apps/docs/content/docs/tools/slack.mdx @@ -86,10 +86,13 @@ Send messages to Slack channels or users through the Slack API. Supports Slack m #### Output -| Parameter | Type | -| --------- | ---- | -| `ts` | string | -| `channel` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ts` | string | Message timestamp | +| `channel` | string | Channel identifier | +| `canvas_id` | string | Canvas identifier | +| `title` | string | Canvas title | +| `messages` | json | Message data | ### `slack_canvas` @@ -109,11 +112,13 @@ Create and share Slack canvases in channels. Canvases are collaborative document #### Output -| Parameter | Type | -| --------- | ---- | -| `canvas_id` | string | -| `channel` | string | -| `title` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ts` | string | Message timestamp | +| `channel` | string | Channel identifier | +| `canvas_id` | string | Canvas identifier | +| `title` | string | Canvas title | +| `messages` | json | Message data | ### `slack_message_reader` @@ -133,33 +138,16 @@ Read the latest messages from Slack channels. Retrieve conversation history with #### Output -| Parameter | Type | -| --------- | ---- | -| `messages` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ts` | string | Message timestamp | +| `channel` | string | Channel identifier | +| `canvas_id` | string | Canvas identifier | +| `title` | string | Canvas title | +| `messages` | json | Message data | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `ts` | string | ts output from the block | -| `channel` | string | channel output from the block | -| `canvas_id` | string | canvas_id output from the block | -| `title` | string | title output from the block | -| `messages` | json | messages output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/stagehand.mdx b/apps/docs/content/docs/tools/stagehand.mdx index d2534b74a..c0fe0e6ca 100644 --- a/apps/docs/content/docs/tools/stagehand.mdx +++ b/apps/docs/content/docs/tools/stagehand.mdx @@ -212,29 +212,12 @@ Extract structured data from a webpage using Stagehand #### Output -| Parameter | Type | -| --------- | ---- | -| `data` | json | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `data` | json | Extracted data | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `url` | string | Yes | URL - Enter the URL of the website to extract data from | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `data` | json | data output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/stagehand_agent.mdx b/apps/docs/content/docs/tools/stagehand_agent.mdx index 40ed4a7ac..3f1b5f20b 100644 --- a/apps/docs/content/docs/tools/stagehand_agent.mdx +++ b/apps/docs/content/docs/tools/stagehand_agent.mdx @@ -217,33 +217,13 @@ Run an autonomous web agent to complete tasks and extract structured data #### Output -| Parameter | Type | -| --------- | ---- | -| `agentResult` | string | -| `completed` | string | -| `message` | string | -| `actions` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `agentResult` | json | Agent execution result | +| `structuredOutput` | any | Structured output data | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `startUrl` | string | Yes | Starting URL - Enter the starting URL for the agent | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `agentResult` | json | agentResult output from the block | -| `structuredOutput` | any | structuredOutput output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/supabase.mdx b/apps/docs/content/docs/tools/supabase.mdx index 9ff0f2e24..772967fc2 100644 --- a/apps/docs/content/docs/tools/supabase.mdx +++ b/apps/docs/content/docs/tools/supabase.mdx @@ -99,10 +99,10 @@ Query data from a Supabase table #### Output -| Parameter | Type | -| --------- | ---- | -| `message` | string | -| `results` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `message` | string | Operation message | +| `results` | json | Query results | ### `supabase_insert` @@ -119,10 +119,10 @@ Insert data into a Supabase table #### Output -| Parameter | Type | -| --------- | ---- | -| `message` | string | -| `results` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `message` | string | Operation message | +| `results` | json | Query results | ### `supabase_get_row` @@ -139,10 +139,10 @@ Get a single row from a Supabase table based on filter criteria #### Output -| Parameter | Type | -| --------- | ---- | -| `message` | string | -| `results` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `message` | string | Operation message | +| `results` | json | Query results | ### `supabase_update` @@ -160,9 +160,10 @@ Update rows in a Supabase table based on filter criteria #### Output -| Parameter | Type | -| --------- | ---- | -| `message` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `message` | string | Operation message | +| `results` | json | Query results | ### `supabase_delete` @@ -179,30 +180,13 @@ Delete rows from a Supabase table based on filter criteria #### Output -| Parameter | Type | -| --------- | ---- | -| `message` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `message` | string | Operation message | +| `results` | json | Query results | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `message` | string | message output from the block | -| `results` | json | results output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/tavily.mdx b/apps/docs/content/docs/tools/tavily.mdx index 292e1d310..3f4ec5b60 100644 --- a/apps/docs/content/docs/tools/tavily.mdx +++ b/apps/docs/content/docs/tools/tavily.mdx @@ -78,13 +78,14 @@ Perform AI-powered web searches using Tavily #### Output -| Parameter | Type | -| --------- | ---- | -| `query` | string | -| `results` | string | -| `url` | string | -| `snippet` | string | -| `raw_content` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results data | +| `answer` | any | Search answer | +| `query` | string | Query used | +| `content` | string | Extracted content | +| `title` | string | Page title | +| `url` | string | Source URL | ### `tavily_extract` @@ -100,35 +101,17 @@ Extract raw content from multiple web pages simultaneously using Tavily #### Output -| Parameter | Type | -| --------- | ---- | -| `results` | string | -| `failed_results` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `results` | json | Search results data | +| `answer` | any | Search answer | +| `query` | string | Query used | +| `content` | string | Extracted content | +| `title` | string | Page title | +| `url` | string | Source URL | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `results` | json | results output from the block | -| `answer` | any | answer output from the block | -| `query` | string | query output from the block | -| `content` | string | content output from the block | -| `title` | string | title output from the block | -| `url` | string | url output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/telegram.mdx b/apps/docs/content/docs/tools/telegram.mdx index fe1fa3285..155d82edf 100644 --- a/apps/docs/content/docs/tools/telegram.mdx +++ b/apps/docs/content/docs/tools/telegram.mdx @@ -87,31 +87,13 @@ Send messages to Telegram channels or users through the Telegram Bot API. Enable #### Output -| Parameter | Type | -| --------- | ---- | -| `ok` | string | -| `date` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ok` | boolean | Success status | +| `result` | json | Message result | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `botToken` | string | Yes | Bot Token - Enter your Telegram Bot Token | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `ok` | boolean | ok output from the block | -| `result` | json | result output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/thinking.mdx b/apps/docs/content/docs/tools/thinking.mdx index ed5f5a70d..917b8340b 100644 --- a/apps/docs/content/docs/tools/thinking.mdx +++ b/apps/docs/content/docs/tools/thinking.mdx @@ -67,29 +67,12 @@ Processes a provided thought/instruction, making it available for subsequent ste #### Output -| Parameter | Type | -| --------- | ---- | -| `acknowledgedThought` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `acknowledgedThought` | string | Acknowledged thought process | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `thought` | string | Yes | Thought Process / Instruction - Describe the step-by-step thinking process here... | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `acknowledgedThought` | string | acknowledgedThought output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/translate.mdx b/apps/docs/content/docs/tools/translate.mdx index 0c92a8d71..27bfa2668 100644 --- a/apps/docs/content/docs/tools/translate.mdx +++ b/apps/docs/content/docs/tools/translate.mdx @@ -63,7 +63,11 @@ Convert text between languages while preserving meaning, nuance, and formatting. #### Output -This tool does not produce any outputs. +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Translated text | +| `model` | string | Model used | +| `tokens` | any | Token usage | ### `anthropic_chat` @@ -77,29 +81,14 @@ This tool does not produce any outputs. #### Output -This tool does not produce any outputs. +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Translated text | +| `model` | string | Model used | +| `tokens` | any | Token usage | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `context` | string | Yes | Text to Translate - Enter the text you want to translate | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `content` | string | content output from the block | -| `model` | string | model output from the block | -| `tokens` | any | tokens output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/twilio_sms.mdx b/apps/docs/content/docs/tools/twilio_sms.mdx index c3c310db0..dfbd529f2 100644 --- a/apps/docs/content/docs/tools/twilio_sms.mdx +++ b/apps/docs/content/docs/tools/twilio_sms.mdx @@ -56,34 +56,15 @@ Send text messages to single or multiple recipients using the Twilio API. #### Output -| Parameter | Type | -| --------- | ---- | -| `success` | string | -| `messageId` | string | -| `status` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Send success status | +| `messageId` | any | Message identifier | +| `status` | any | Delivery status | +| `error` | any | Error information | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `phoneNumbers` | string | Yes | Recipient Phone Numbers - Enter phone numbers with country code \(one per line, e.g., +1234567890\) | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `success` | boolean | success output from the block | -| `messageId` | any | messageId output from the block | -| `status` | any | status output from the block | -| `error` | any | error output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/typeform.mdx b/apps/docs/content/docs/tools/typeform.mdx index 2b550dc00..21550595b 100644 --- a/apps/docs/content/docs/tools/typeform.mdx +++ b/apps/docs/content/docs/tools/typeform.mdx @@ -70,14 +70,11 @@ Retrieve form responses from Typeform #### Output -| Parameter | Type | -| --------- | ---- | -| `total_items` | string | -| `answers` | string | -| `type` | string | -| `hidden` | string | -| `calculated` | string | -| `variables` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `total_items` | number | Total response count | +| `page_count` | number | Total page count | +| `items` | json | Response items | ### `typeform_files` @@ -96,11 +93,11 @@ Download files uploaded in Typeform responses #### Output -| Parameter | Type | -| --------- | ---- | -| `fileUrl` | string | -| `contentType` | string | -| `filename` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `total_items` | number | Total response count | +| `page_count` | number | Total page count | +| `items` | json | Response items | ### `typeform_insights` @@ -115,29 +112,14 @@ Retrieve insights and analytics for Typeform forms #### Output -This tool does not produce any outputs. +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `total_items` | number | Total response count | +| `page_count` | number | Total page count | +| `items` | json | Response items | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `total_items` | number | total_items output from the block | -| `page_count` | number | page_count output from the block | -| `items` | json | items output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/vision.mdx b/apps/docs/content/docs/tools/vision.mdx index c638dd2ee..cf1ea47ab 100644 --- a/apps/docs/content/docs/tools/vision.mdx +++ b/apps/docs/content/docs/tools/vision.mdx @@ -68,33 +68,14 @@ Process and analyze images using advanced vision models. Capable of understandin #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | -| `model` | string | -| `tokens` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | string | Analysis result | +| `model` | any | Model used | +| `tokens` | any | Token usage | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `content` | string | content output from the block | -| `model` | any | model output from the block | -| `tokens` | any | tokens output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/wealthbox.mdx b/apps/docs/content/docs/tools/wealthbox.mdx index 705009535..d8cc291c5 100644 --- a/apps/docs/content/docs/tools/wealthbox.mdx +++ b/apps/docs/content/docs/tools/wealthbox.mdx @@ -61,12 +61,16 @@ Read content from a Wealthbox note #### Output -| Parameter | Type | -| --------- | ---- | -| `note` | string | -| `metadata` | string | -| `noteId` | string | -| `itemType` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `note` | any | Note data | +| `notes` | any | Notes list | +| `contact` | any | Contact data | +| `contacts` | any | Contacts list | +| `task` | any | Task data | +| `tasks` | any | Tasks list | +| `metadata` | json | Operation metadata | +| `success` | any | Success status | ### `wealthbox_write_note` @@ -82,9 +86,16 @@ Create or update a Wealthbox note #### Output -| Parameter | Type | -| --------- | ---- | -| `data` | json | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `note` | any | Note data | +| `notes` | any | Notes list | +| `contact` | any | Contact data | +| `contacts` | any | Contacts list | +| `task` | any | Task data | +| `tasks` | any | Tasks list | +| `metadata` | json | Operation metadata | +| `success` | any | Success status | ### `wealthbox_read_contact` @@ -99,12 +110,16 @@ Read content from a Wealthbox contact #### Output -| Parameter | Type | -| --------- | ---- | -| `contact` | string | -| `metadata` | string | -| `contactId` | string | -| `itemType` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `note` | any | Note data | +| `notes` | any | Notes list | +| `contact` | any | Contact data | +| `contacts` | any | Contacts list | +| `task` | any | Task data | +| `tasks` | any | Tasks list | +| `metadata` | json | Operation metadata | +| `success` | any | Success status | ### `wealthbox_write_contact` @@ -122,11 +137,16 @@ Create a new Wealthbox contact #### Output -| Parameter | Type | -| --------- | ---- | -| `contact` | string | -| `metadata` | string | -| `itemType` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `note` | any | Note data | +| `notes` | any | Notes list | +| `contact` | any | Contact data | +| `contacts` | any | Contacts list | +| `task` | any | Task data | +| `tasks` | any | Tasks list | +| `metadata` | json | Operation metadata | +| `success` | any | Success status | ### `wealthbox_read_task` @@ -141,12 +161,16 @@ Read content from a Wealthbox task #### Output -| Parameter | Type | -| --------- | ---- | -| `task` | string | -| `metadata` | string | -| `taskId` | string | -| `itemType` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `note` | any | Note data | +| `notes` | any | Notes list | +| `contact` | any | Contact data | +| `contacts` | any | Contacts list | +| `task` | any | Task data | +| `tasks` | any | Tasks list | +| `metadata` | json | Operation metadata | +| `success` | any | Success status | ### `wealthbox_write_task` @@ -164,36 +188,19 @@ Create or update a Wealthbox task #### Output -| Parameter | Type | -| --------- | ---- | -| `data` | json | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `note` | any | Note data | +| `notes` | any | Notes list | +| `contact` | any | Contact data | +| `contacts` | any | Contacts list | +| `task` | any | Task data | +| `tasks` | any | Tasks list | +| `metadata` | json | Operation metadata | +| `success` | any | Success status | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `note` | any | note output from the block | -| `notes` | any | notes output from the block | -| `contact` | any | contact output from the block | -| `contacts` | any | contacts output from the block | -| `task` | any | task output from the block | -| `tasks` | any | tasks output from the block | -| `metadata` | json | metadata output from the block | -| `success` | any | success output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/webhook.mdx b/apps/docs/content/docs/tools/webhook.mdx index 25fad519f..66b557d54 100644 --- a/apps/docs/content/docs/tools/webhook.mdx +++ b/apps/docs/content/docs/tools/webhook.mdx @@ -26,20 +26,6 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `webhookProvider` | dropdown | Yes | Webhook Provider | - - - -### Outputs - -This block does not produce any outputs. - ## Notes - Category: `triggers` diff --git a/apps/docs/content/docs/tools/whatsapp.mdx b/apps/docs/content/docs/tools/whatsapp.mdx index 29517bdc0..a8b3e9675 100644 --- a/apps/docs/content/docs/tools/whatsapp.mdx +++ b/apps/docs/content/docs/tools/whatsapp.mdx @@ -58,32 +58,14 @@ Send WhatsApp messages #### Output -| Parameter | Type | -| --------- | ---- | -| `success` | string | -| `messageId` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Send success status | +| `messageId` | any | Message identifier | +| `error` | any | Error information | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `phoneNumber` | string | Yes | Recipient Phone Number - Enter phone number with country code \(e.g., +1234567890\) | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `success` | boolean | success output from the block | -| `messageId` | any | messageId output from the block | -| `error` | any | error output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/wikipedia.mdx b/apps/docs/content/docs/tools/wikipedia.mdx index 4568159ee..6556945e5 100644 --- a/apps/docs/content/docs/tools/wikipedia.mdx +++ b/apps/docs/content/docs/tools/wikipedia.mdx @@ -72,20 +72,13 @@ Get a summary and metadata for a specific Wikipedia page. #### Output -| Parameter | Type | -| --------- | ---- | -| `summary` | string | -| `title` | string | -| `displaytitle` | string | -| `description` | string | -| `extract` | string | -| `extract_html` | string | -| `thumbnail` | string | -| `originalimage` | string | -| `content_urls` | string | -| `revisions` | string | -| `edit` | string | -| `talk` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `summary` | json | Page summary data | +| `searchResults` | json | Search results data | +| `totalHits` | number | Total search hits | +| `content` | json | Page content data | +| `randomPage` | json | Random page data | ### `wikipedia_search` @@ -100,11 +93,13 @@ Search for Wikipedia pages by title or content. #### Output -| Parameter | Type | -| --------- | ---- | -| `totalHits` | string | -| `query` | string | -| `searchResults` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `summary` | json | Page summary data | +| `searchResults` | json | Search results data | +| `totalHits` | number | Total search hits | +| `content` | json | Page content data | +| `randomPage` | json | Random page data | ### `wikipedia_content` @@ -118,16 +113,13 @@ Get the full HTML content of a Wikipedia page. #### Output -| Parameter | Type | -| --------- | ---- | -| `content` | string | -| `pageid` | string | -| `html` | string | -| `revision` | string | -| `tid` | string | -| `timestamp` | string | -| `content_model` | string | -| `content_format` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `summary` | json | Page summary data | +| `searchResults` | json | Search results data | +| `totalHits` | number | Total search hits | +| `content` | json | Page content data | +| `randomPage` | json | Random page data | ### `wikipedia_random` @@ -140,39 +132,16 @@ Get a random Wikipedia page. #### Output -| Parameter | Type | -| --------- | ---- | -| `randomPage` | string | -| `title` | string | -| `displaytitle` | string | -| `description` | string | -| `extract` | string | -| `thumbnail` | string | -| `content_urls` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `summary` | json | Page summary data | +| `searchResults` | json | Search results data | +| `totalHits` | number | Total search hits | +| `content` | json | Page content data | +| `randomPage` | json | Random page data | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `summary` | json | summary output from the block | -| `searchResults` | json | searchResults output from the block | -| `totalHits` | number | totalHits output from the block | -| `content` | json | content output from the block | -| `randomPage` | json | randomPage output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/x.mdx b/apps/docs/content/docs/tools/x.mdx index 326a6d458..e6c99d045 100644 --- a/apps/docs/content/docs/tools/x.mdx +++ b/apps/docs/content/docs/tools/x.mdx @@ -58,16 +58,16 @@ Post new tweets, reply to tweets, or create polls on X (Twitter) #### Output -| Parameter | Type | -| --------- | ---- | -| `tweet` | string | -| `text` | string | -| `createdAt` | string | -| `authorId` | string | -| `conversationId` | string | -| `inReplyToUserId` | string | -| `attachments` | string | -| `pollId` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `tweet` | json | Tweet data | +| `replies` | any | Tweet replies | +| `context` | any | Tweet context | +| `tweets` | json | Tweets data | +| `includes` | any | Additional data | +| `meta` | json | Response metadata | +| `user` | json | User profile data | +| `recentTweets` | any | Recent tweets data | ### `x_read` @@ -83,10 +83,16 @@ Read tweet details, including replies and conversation context #### Output -| Parameter | Type | -| --------- | ---- | -| `tweet` | string | -| `context` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `tweet` | json | Tweet data | +| `replies` | any | Tweet replies | +| `context` | any | Tweet context | +| `tweets` | json | Tweets data | +| `includes` | any | Additional data | +| `meta` | json | Response metadata | +| `user` | json | User profile data | +| `recentTweets` | any | Recent tweets data | ### `x_search` @@ -105,12 +111,16 @@ Search for tweets using keywords, hashtags, or advanced queries #### Output -| Parameter | Type | -| --------- | ---- | -| `tweets` | string | -| `includes` | string | -| `media` | string | -| `polls` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `tweet` | json | Tweet data | +| `replies` | any | Tweet replies | +| `context` | any | Tweet context | +| `tweets` | json | Tweets data | +| `includes` | any | Additional data | +| `meta` | json | Response metadata | +| `user` | json | User profile data | +| `recentTweets` | any | Recent tweets data | ### `x_user` @@ -125,36 +135,19 @@ Get user profile information #### Output -| Parameter | Type | -| --------- | ---- | -| `user` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `tweet` | json | Tweet data | +| `replies` | any | Tweet replies | +| `context` | any | Tweet context | +| `tweets` | json | Tweets data | +| `includes` | any | Additional data | +| `meta` | json | Response metadata | +| `user` | json | User profile data | +| `recentTweets` | any | Recent tweets data | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `operation` | string | Yes | Operation | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `tweet` | json | tweet output from the block | -| `replies` | any | replies output from the block | -| `context` | any | context output from the block | -| `tweets` | json | tweets output from the block | -| `includes` | any | includes output from the block | -| `meta` | json | meta output from the block | -| `user` | json | user output from the block | -| `recentTweets` | any | recentTweets output from the block | - - ## Notes - Category: `tools` diff --git a/apps/docs/content/docs/tools/youtube.mdx b/apps/docs/content/docs/tools/youtube.mdx index 75235d777..6f8ddda34 100644 --- a/apps/docs/content/docs/tools/youtube.mdx +++ b/apps/docs/content/docs/tools/youtube.mdx @@ -60,32 +60,13 @@ Search for videos on YouTube using the YouTube Data API. #### Output -| Parameter | Type | -| --------- | ---- | -| `totalResults` | string | -| `nextPageToken` | string | -| `items` | string | +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `items` | json | The items returned by the YouTube search | +| `totalResults` | number | The total number of results returned by the YouTube search | -## Block Configuration - -### Input - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `apiKey` | string | Yes | YouTube API Key - Enter YouTube API Key | - - - -### Outputs - -| Output | Type | Description | -| ------ | ---- | ----------- | -| `items` | json | items output from the block | -| `totalResults` | number | totalResults output from the block | - - ## Notes - Category: `tools` diff --git a/apps/sim/app/api/chat/utils.ts b/apps/sim/app/api/chat/utils.ts index 6d66bf749..b384ded04 100644 --- a/apps/sim/app/api/chat/utils.ts +++ b/apps/sim/app/api/chat/utils.ts @@ -470,7 +470,8 @@ export async function executeWorkflowForChat( mergedStates, edges, loops, - parallels + parallels, + true // Enable validation during execution ) // Decrypt environment variables diff --git a/apps/sim/app/api/proxy/route.ts b/apps/sim/app/api/proxy/route.ts index e40dc91cf..95c6d9997 100644 --- a/apps/sim/app/api/proxy/route.ts +++ b/apps/sim/app/api/proxy/route.ts @@ -2,7 +2,7 @@ import { NextResponse } from 'next/server' import { isDev } from '@/lib/environment' import { createLogger } from '@/lib/logs/console/logger' import { executeTool } from '@/tools' -import { getTool, validateToolRequest } from '@/tools/utils' +import { getTool, validateRequiredParametersAfterMerge } from '@/tools/utils' const logger = createLogger('ProxyAPI') @@ -196,7 +196,7 @@ export async function POST(request: Request) { // Validate the tool and its parameters try { - validateToolRequest(toolId, tool, params) + validateRequiredParametersAfterMerge(toolId, tool, params) } catch (validationError) { logger.warn(`[${requestId}] Tool validation failed for ${toolId}`, { error: validationError instanceof Error ? validationError.message : String(validationError), diff --git a/apps/sim/app/api/schedules/execute/route.ts b/apps/sim/app/api/schedules/execute/route.ts index 1b0b3799e..0f6cd2838 100644 --- a/apps/sim/app/api/schedules/execute/route.ts +++ b/apps/sim/app/api/schedules/execute/route.ts @@ -376,7 +376,8 @@ export async function GET() { mergedStates, edges, loops, - parallels + parallels, + true // Enable validation during execution ) const input = { diff --git a/apps/sim/app/api/workflows/[id]/execute/route.ts b/apps/sim/app/api/workflows/[id]/execute/route.ts index bb19cefa2..893a5efa6 100644 --- a/apps/sim/app/api/workflows/[id]/execute/route.ts +++ b/apps/sim/app/api/workflows/[id]/execute/route.ts @@ -281,7 +281,8 @@ async function executeWorkflow(workflow: any, requestId: string, input?: any): P mergedStates, edges, loops, - parallels + parallels, + true // Enable validation during execution ) const executor = new Executor( diff --git a/apps/sim/app/globals.css b/apps/sim/app/globals.css index 27d1b85a1..aa95668db 100644 --- a/apps/sim/app/globals.css +++ b/apps/sim/app/globals.css @@ -58,7 +58,7 @@ --muted-foreground: 0 0% 46.9%; /* Accent Colors */ - --accent: 0 0% 96.1%; + --accent: 0 0% 92.5%; --accent-foreground: 0 0% 11.2%; /* Destructive Colors */ diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/upload-modal/upload-modal.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/upload-modal/upload-modal.tsx index 8bfae5432..29195f138 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/upload-modal/upload-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/upload-modal/upload-modal.tsx @@ -187,6 +187,10 @@ export function UploadModal({ disabled={isUploading} knowledgeBaseId={knowledgeBaseId} documentId={null} // No specific document for upload + onSave={async () => { + // For upload modal, tags are saved when document is uploaded + // This is a placeholder as tags will be applied during upload + }} /> {/* File Upload Section */} diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/components/document-tag-entry/document-tag-entry.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/components/document-tag-entry/document-tag-entry.tsx index ab3aa91d1..9985842ab 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/components/document-tag-entry/document-tag-entry.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/components/document-tag-entry/document-tag-entry.tsx @@ -1,29 +1,23 @@ 'use client' -import { useEffect, useRef, useState } from 'react' +import { useState } from 'react' import { ChevronDown, Plus, X } from 'lucide-react' -import { Button } from '@/components/ui/button' import { + Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu' -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select' + formatDisplayText, + Input, + Label, +} from '@/components/ui' import { MAX_TAG_SLOTS, TAG_SLOTS, type TagSlot } from '@/lib/constants/knowledge' import { useKnowledgeBaseTagDefinitions } from '@/hooks/use-knowledge-base-tag-definitions' import { type TagDefinitionInput, useTagDefinitions } from '@/hooks/use-tag-definitions' export interface DocumentTag { - slot: TagSlot + slot: string displayName: string fieldType: string value: string @@ -31,21 +25,19 @@ export interface DocumentTag { interface DocumentTagEntryProps { tags: DocumentTag[] - onTagsChange: (tags: DocumentTag[]) => void + onTagsChange: (newTags: DocumentTag[]) => void disabled?: boolean - knowledgeBaseId?: string | null - documentId?: string | null - onSave?: (tags: DocumentTag[]) => Promise + knowledgeBaseId: string + documentId: string | null + onSave: (tagsToSave: DocumentTag[]) => Promise } -// TAG_SLOTS is now imported from constants - export function DocumentTagEntry({ tags, onTagsChange, disabled = false, - knowledgeBaseId = null, - documentId = null, + knowledgeBaseId, + documentId, onSave, }: DocumentTagEntryProps) { const { saveTagDefinitions } = useTagDefinitions(knowledgeBaseId, documentId) @@ -66,389 +58,232 @@ export function DocumentTagEntry({ return slot } } - return 'tag1' // fallback + return TAG_SLOTS[0] // Fallback to first slot if all are used } - const handleSaveDefinitions = async (tagsToSave?: DocumentTag[]) => { - if (!knowledgeBaseId || !documentId) return - - const currentTags = tagsToSave || tags - - // Create definitions for tags that have display names - const definitions: TagDefinitionInput[] = currentTags - .filter((tag) => tag?.displayName?.trim()) - .map((tag) => ({ - tagSlot: tag.slot as TagSlot, - displayName: tag.displayName.trim(), - fieldType: tag.fieldType || 'text', - })) - - // Only save if we have valid definitions - if (definitions.length > 0) { - await saveTagDefinitions(definitions) - } - } - - const handleCleanupUnusedTags = async () => { - if (!knowledgeBaseId || !documentId) return - - try { - const response = await fetch( - `/api/knowledge/${knowledgeBaseId}/documents/${documentId}/tag-definitions?action=cleanup`, - { - method: 'DELETE', - } - ) - - if (!response.ok) { - throw new Error(`Cleanup failed: ${response.statusText}`) - } - - const result = await response.json() - console.log('Cleanup result:', result) - } catch (error) { - console.error('Failed to cleanup unused tags:', error) - } - } - - // Get available tag names that aren't already used in this document - const availableTagNames = kbTagDefinitions - .map((tag) => tag.displayName) - .filter((tagName) => !tags.some((tag) => tag.displayName === tagName)) - - // Check if we can add more tags (KB has less than MAX_TAG_SLOTS tag definitions) - const canAddMoreTags = kbTagDefinitions.length < MAX_TAG_SLOTS - - const handleSuggestionClick = (tagName: string) => { - setEditingTag({ index: -1, value: '', tagName, isNew: false }) - } - - const handleCreateNewTag = async (tagName: string, value: string, fieldType = 'text') => { - if (!tagName.trim() || !value.trim()) return - - // Check if tag name already exists in current document - const tagNameLower = tagName.trim().toLowerCase() - const existingTag = tags.find((tag) => tag.displayName.toLowerCase() === tagNameLower) - if (existingTag) { - alert(`Tag "${tagName}" already exists. Please choose a different name.`) - return - } + const handleAddTag = () => { + if (tags.length >= MAX_TAG_SLOTS) return const newTag: DocumentTag = { slot: getNextAvailableSlot(), - displayName: tagName.trim(), - fieldType: fieldType, - value: value.trim(), + displayName: '', + fieldType: 'text', + value: '', } const updatedTags = [...tags, newTag] + onTagsChange(updatedTags) - // SIMPLE ATOMIC OPERATION - NO CLEANUP - try { - // 1. Save tag definition first - await handleSaveDefinitions(updatedTags) - - // 2. Save document values - if (onSave) { - await onSave(updatedTags) - } - - // 3. Update UI - onTagsChange(updatedTags) - } catch (error) { - console.error('Failed to save tag:', error) - alert(`Failed to save tag "${tagName}". Please try again.`) - } + // Set editing state for the new tag + setEditingTag({ + index: updatedTags.length - 1, + value: '', + tagName: '', + isNew: true, + }) } - const handleUpdateTag = async (index: number, newValue: string) => { - if (!newValue.trim()) return + const handleRemoveTag = (index: number) => { + const updatedTags = tags.filter((_, i) => i !== index) + onTagsChange(updatedTags) + } - const updatedTags = tags.map((tag, i) => - i === index ? { ...tag, value: newValue.trim() } : tag + const handleTagUpdate = (index: number, field: keyof DocumentTag, value: string) => { + const updatedTags = [...tags] + updatedTags[index] = { ...updatedTags[index], [field]: value } + onTagsChange(updatedTags) + } + + const handleSaveTag = async (index: number, tagName: string) => { + if (!tagName.trim()) return + + // Check if this is creating a new tag definition + const existingDefinition = kbTagDefinitions.find( + (def) => def.displayName.toLowerCase() === tagName.toLowerCase() ) - // SIMPLE ATOMIC OPERATION - NO CLEANUP - try { - // 1. Save document values - if (onSave) { - await onSave(updatedTags) + if (!existingDefinition) { + // Create new tag definition + const newDefinition: TagDefinitionInput = { + displayName: tagName, + fieldType: 'text', + tagSlot: tags[index].slot as TagSlot, } - // 2. Save tag definitions - await handleSaveDefinitions(updatedTags) - // 3. Update UI - onTagsChange(updatedTags) + + try { + await saveTagDefinitions([newDefinition]) + await refreshTagDefinitions() + } catch (error) { + console.error('Failed to save tag definition:', error) + return + } + } + + // Update the tag + handleTagUpdate(index, 'displayName', tagName) + setEditingTag(null) + } + + const handleCancelEdit = () => { + if (editingTag?.isNew) { + // Remove the new tag if editing was cancelled + handleRemoveTag(editingTag.index) + } + setEditingTag(null) + } + + const handleSaveAll = async () => { + try { + await onSave(tags) } catch (error) { - console.error('Failed to update tag:', error) + console.error('Failed to save tags:', error) } } - const handleRemoveTag = async (index: number) => { - const updatedTags = tags.filter((_, i) => i !== index) - - console.log('Removing tag, updated tags:', updatedTags) - - // FULLY SYNCHRONOUS - DO NOT UPDATE UI UNTIL ALL OPERATIONS COMPLETE - try { - // 1. Save the document tag values - console.log('Saving document values after tag removal...') - if (onSave) { - await onSave(updatedTags) - } - - // 2. Save the tag definitions - console.log('Saving tag definitions after tag removal...') - await handleSaveDefinitions(updatedTags) - - // 3. Run cleanup to remove unused tag definitions - console.log('Running cleanup to remove unused tag definitions...') - await handleCleanupUnusedTags() - - // 4. ONLY NOW update the UI - onTagsChange(updatedTags) - - // 5. Refresh tag definitions for dropdown - await refreshTagDefinitions() - } catch (error) { - console.error('Failed to remove tag:', error) - } - } + // Filter available tag definitions (exclude already used ones) + const availableDefinitions = kbTagDefinitions.filter( + (def) => !tags.some((tag) => tag.displayName.toLowerCase() === def.displayName.toLowerCase()) + ) return ( -
- {/* Existing Tags as Chips */} -
- {tags.map((tag, index) => ( -
- setEditingTag({ index, value: tag.value, tagName: tag.displayName, isNew: false }) - } - > - {tag.displayName}: - {tag.value} - -
- ))} -
- - {/* Add Tag Dropdown Selector */} - - +
+
+

Document Tags

+
- - - {/* Existing tag names */} - {availableTagNames.length > 0 && ( - <> - {availableTagNames.map((tagName) => { - const tagDefinition = kbTagDefinitions.find((def) => def.displayName === tagName) - return ( - handleSuggestionClick(tagName)} - className='flex items-center justify-between' - > - {tagName} - - {tagDefinition?.fieldType || 'text'} - - - ) - })} -
- - )} + +
+
- {/* Create new tag option or disabled message */} - {canAddMoreTags ? ( - { - setEditingTag({ index: -1, value: '', tagName: '', isNew: true }) - }} - className='flex items-center gap-2 text-blue-600' - > - - Create new tag - - ) : ( -
- All {MAX_TAG_SLOTS} tag slots used in this knowledge base + {tags.length === 0 ? ( +
+

No tags added yet

+
+ ) : ( +
+ {tags.map((tag, index) => ( +
+
+ {editingTag?.index === index ? ( +
+
+ setEditingTag({ ...editingTag, tagName: e.target.value })} + placeholder='Tag name' + className='flex-1' + autoFocus + /> + + + + + + {availableDefinitions.map((def) => ( + + setEditingTag({ ...editingTag, tagName: def.displayName }) + } + > + {def.displayName} + + ))} + {availableDefinitions.length === 0 && ( + No available tags + )} + + +
+
+ + +
+
+ ) : ( +
+
+
+
+ {tag.displayName || 'Unnamed Tag'} +
+
+ Slot: {tag.slot} • Type: {tag.fieldType} +
+
+ +
+
+ +
+ handleTagUpdate(index, 'value', e.target.value)} + placeholder='Enter tag value' + disabled={disabled} + className='w-full text-transparent caret-foreground' + /> +
+
{formatDisplayText(tag.value)}
+
+
+
+
+ )} +
+
- )} - - - - {/* Edit Tag Value Modal */} - {editingTag !== null && ( - t.displayName === editingTag.tagName)?.fieldType - } - onSave={(value, type, newTagName) => { - if (editingTag.index === -1) { - // Creating new tag - use newTagName if provided, otherwise fall back to editingTag.tagName - const tagName = newTagName || editingTag.tagName - handleCreateNewTag(tagName, value, type) - } else { - // Updating existing tag - handleUpdateTag(editingTag.index, value) - } - setEditingTag(null) - }} - onCancel={() => { - setEditingTag(null) - }} - /> - )} - - {/* Tag count display */} - {kbTagDefinitions.length > 0 && ( -
- {kbTagDefinitions.length} of {MAX_TAG_SLOTS} tag slots used in this knowledge base + ))}
)} -
- ) -} -// Simple modal for editing tag values -interface EditTagModalProps { - tagName: string - initialValue: string - isNew: boolean - existingType?: string - onSave: (value: string, type?: string, newTagName?: string) => void - onCancel: () => void -} - -function EditTagModal({ - tagName, - initialValue, - isNew, - existingType, - onSave, - onCancel, -}: EditTagModalProps) { - const [value, setValue] = useState(initialValue) - const [fieldType, setFieldType] = useState(existingType || 'text') - const [newTagName, setNewTagName] = useState(tagName) - const inputRef = useRef(null) - - useEffect(() => { - inputRef.current?.focus() - }, []) - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault() - if (value.trim() && (isNew ? newTagName.trim() : true)) { - onSave(value.trim(), fieldType, isNew ? newTagName.trim() : undefined) - } - } - - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Escape') { - onCancel() - } - } - - return ( -
-
-
-

- {isNew ? 'Create new tag' : `Edit "${tagName}" value`} -

- {/* Type Badge in Top Right */} - {!isNew && existingType && ( - - {existingType.toUpperCase()} - - )} -
-
- {/* Tag Name Input for New Tags */} - {isNew && ( -
- - setNewTagName(e.target.value)} - placeholder='Enter tag name' - className='mt-1 text-sm' - /> -
- )} - - {/* Type Selection for New Tags */} - {isNew && ( -
- - -
- )} - - {/* Value Input */} -
- - setValue(e.target.value)} - onKeyDown={handleKeyDown} - placeholder='Enter tag value' - className='mt-1 text-sm' - /> -
- -
- - -
-
+
+ {tags.length} of {MAX_TAG_SLOTS} tag slots used
) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/document-tag-entry/document-tag-entry.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/document-tag-entry/document-tag-entry.tsx index f20d16fa0..10a266da8 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/document-tag-entry/document-tag-entry.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/document-tag-entry/document-tag-entry.tsx @@ -3,6 +3,7 @@ import { useMemo, useState } from 'react' import { Plus, Trash2 } from 'lucide-react' import { Button } from '@/components/ui/button' +import { formatDisplayText } from '@/components/ui/formatted-text' import { Input } from '@/components/ui/input' import { Select, @@ -11,11 +12,19 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select' +import { checkTagTrigger, TagDropdown } from '@/components/ui/tag-dropdown' import { MAX_TAG_SLOTS } from '@/lib/constants/knowledge' import { cn } from '@/lib/utils' +import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/hooks/use-sub-block-value' import type { SubBlockConfig } from '@/blocks/types' import { useKnowledgeBaseTagDefinitions } from '@/hooks/use-knowledge-base-tag-definitions' -import { useSubBlockValue } from '../../hooks/use-sub-block-value' + +export interface DocumentTag { + slot: string + displayName: string + fieldType: string + value: string +} interface DocumentTagRow { id: string @@ -55,6 +64,15 @@ export function DocumentTagEntry({ // State for dropdown visibility - one for each row const [dropdownStates, setDropdownStates] = useState>({}) + // State for managing tag dropdown + const [activeTagDropdown, setActiveTagDropdown] = useState<{ + rowIndex: number + showTags: boolean + cursorPosition: number + activeSourceBlockId: string | null + element?: HTMLElement | null + } | null>(null) + // Use preview value when in preview mode, otherwise use store value const currentValue = isPreview ? previewValue : storeValue @@ -305,8 +323,6 @@ export function DocumentTagEntry({ Text - Number - Date @@ -318,11 +334,52 @@ export function DocumentTagEntry({ return ( - handleCellChange(rowIndex, 'value', e.target.value)} - disabled={disabled || isConnecting} - /> +
+ { + const newValue = e.target.value + const cursorPosition = e.target.selectionStart ?? 0 + + handleCellChange(rowIndex, 'value', newValue) + + // Check for tag trigger + const tagTrigger = checkTagTrigger(newValue, cursorPosition) + + setActiveTagDropdown({ + rowIndex, + showTags: tagTrigger.show, + cursorPosition, + activeSourceBlockId: null, + element: e.target, + }) + }} + onFocus={(e) => { + if (!disabled && !isConnecting) { + setActiveTagDropdown({ + rowIndex, + showTags: false, + cursorPosition: 0, + activeSourceBlockId: null, + element: e.target, + }) + } + }} + onBlur={() => { + setTimeout(() => setActiveTagDropdown(null), 200) + }} + onKeyDown={(e) => { + if (e.key === 'Escape') { + setActiveTagDropdown(null) + } + }} + disabled={disabled || isConnecting} + className='w-full border-0 text-transparent caret-foreground placeholder:text-muted-foreground/50 focus-visible:ring-0 focus-visible:ring-offset-0' + /> +
+
{formatDisplayText(cellValue)}
+
+
) } @@ -379,16 +436,35 @@ export function DocumentTagEntry({
- {/* Add Row Button */} + {/* Tag Dropdown */} + {activeTagDropdown?.element && ( + { + handleCellChange(activeTagDropdown.rowIndex, 'value', newValue) + setActiveTagDropdown(null) + }} + blockId={blockId} + activeSourceBlockId={activeTagDropdown.activeSourceBlockId} + inputValue={rows[activeTagDropdown.rowIndex]?.cells.value || ''} + cursorPosition={activeTagDropdown.cursorPosition} + onClose={() => { + setActiveTagDropdown((prev) => (prev ? { ...prev, showTags: false } : null)) + }} + className='absolute z-[9999] mt-0' + /> + )} + + {/* Add Row Button and Tag slots usage indicator */} {!isPreview && !disabled && ( -
+
{/* Tag slots usage indicator */} -
+
{tagDefinitions.length + newTagsBeingCreated} of {MAX_TAG_SLOTS} tag slots used
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/tool-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/tool-input.tsx index c4aac84f4..fbf887528 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/tool-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/tool-input.tsx @@ -416,7 +416,9 @@ export function ToolInput({ const provider = model ? getProviderFromModel(model) : '' const supportsToolControl = provider ? supportsToolUsageControl(provider) : false - const toolBlocks = getAllBlocks().filter((block) => block.category === 'tools') + const toolBlocks = getAllBlocks().filter( + (block) => block.category === 'tools' && block.type !== 'evaluator' + ) // Use preview value when in preview mode, otherwise use store value const value = isPreview ? previewValue : storeValue @@ -719,7 +721,21 @@ export function ToolInput({ // Initialize parameters for the new operation const initialParams = initializeToolParams(newToolId, toolParams.userInputParameters, blockId) - // Clear fields when operation changes for Jira + // Preserve ALL existing parameters that also exist in the new tool configuration + // This mimics how regular blocks work - each field maintains its state independently + const oldToolParams = getToolParametersConfig(tool.toolId, tool.type) + const oldParamIds = new Set(oldToolParams?.userInputParameters.map((p) => p.id) || []) + const newParamIds = new Set(toolParams.userInputParameters.map((p) => p.id)) + + // Preserve any parameter that exists in both configurations and has a value + const preservedParams: Record = {} + Object.entries(tool.params).forEach(([paramId, value]) => { + if (newParamIds.has(paramId) && value) { + preservedParams[paramId] = value + } + }) + + // Clear fields when operation changes for Jira (special case) if (tool.type === 'jira') { const subBlockStore = useSubBlockStore.getState() // Clear all fields that might be shared between operations @@ -737,7 +753,7 @@ export function ToolInput({ ...tool, toolId: newToolId, operation, - params: initialParams, // Reset params when operation changes + params: { ...initialParams, ...preservedParams }, // Preserve all compatible existing values } : tool ) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/sub-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/sub-block.tsx index dcd805e05..20fc9e77d 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/sub-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/sub-block.tsx @@ -29,9 +29,7 @@ import { ToolInput, WebhookConfig, } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components' -import { getBlock } from '@/blocks/index' import type { SubBlockConfig } from '@/blocks/types' -import { useWorkflowStore } from '@/stores/workflows/workflow/store' import { DocumentTagEntry } from './components/document-tag-entry/document-tag-entry' import { KnowledgeTagFilter } from './components/knowledge-tag-filter/knowledge-tag-filter' import { KnowledgeTagFilters } from './components/knowledge-tag-filters/knowledge-tag-filters' @@ -64,13 +62,7 @@ export function SubBlock({ } const isFieldRequired = () => { - const blockType = useWorkflowStore.getState().blocks[blockId]?.type - if (!blockType) return false - - const blockConfig = getBlock(blockType) - if (!blockConfig) return false - - return blockConfig.inputs[config.id]?.required === true + return config.required === true } // Get preview value for this specific sub-block diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx index 6428e423f..cf4c3afb6 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx @@ -705,9 +705,13 @@ export function WorkflowBlock({ id, data }: NodeProps) { {Object.entries(config.outputs).map(([key, value]) => (
{key}{' '} - {typeof value === 'object' ? ( + {typeof value === 'object' && value !== null && 'type' in value ? ( + // New format: { type: 'string', description: '...' } + {value.type} + ) : typeof value === 'object' && value !== null ? ( + // Legacy complex object format
- {Object.entries(value.type).map(([typeKey, typeValue]) => ( + {Object.entries(value).map(([typeKey, typeValue]) => (
{typeKey}: @@ -719,6 +723,7 @@ export function WorkflowBlock({ id, data }: NodeProps) { ))}
) : ( + // Old format: just a string {value as string} )}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts index 2780feff6..36e96d234 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts @@ -500,7 +500,8 @@ export function useWorkflowExecution() { filteredStates, filteredEdges, loops, - parallels + parallels, + true // Enable validation during execution ) // Determine if this is a chat execution @@ -587,6 +588,54 @@ export function useWorkflowExecution() { setIsDebugging(false) setActiveBlocks(new Set()) + // Add the error to the console so users can see what went wrong + // This ensures serialization errors appear in the console just like execution errors + if (activeWorkflowId) { + const consoleStore = useConsoleStore.getState() + + // Try to extract block information from the error message + // Serialization errors typically have format: "BlockName is missing required fields: FieldName" + let blockName = 'Workflow Execution' + let blockId = 'workflow-error' + let blockType = 'workflow' + + const blockErrorMatch = errorMessage.match(/^(.+?)\s+is missing required fields/) + if (blockErrorMatch) { + const failedBlockName = blockErrorMatch[1] + blockName = failedBlockName + + // Try to find the actual block in the current workflow to get its ID and type + const allBlocks = Object.values(blocks) + const failedBlock = allBlocks.find((block) => block.name === failedBlockName) + if (failedBlock) { + blockId = failedBlock.id + blockType = failedBlock.type + } else { + // Fallback: use the block name as ID if we can't find the actual block + blockId = failedBlockName.toLowerCase().replace(/\s+/g, '-') + blockType = 'unknown' + } + } + + consoleStore.addConsole({ + workflowId: activeWorkflowId, + blockId: blockId, + blockName: blockName, + blockType: blockType, + success: false, + error: errorMessage, + output: {}, + startedAt: new Date().toISOString(), + endedAt: new Date().toISOString(), + durationMs: 0, + }) + + // Auto-open the console so users can see the error (only if it's not already open) + if (!consoleStore.isOpen) { + toggleConsole() + } + } + let notificationMessage = 'Workflow execution failed' if (error?.request?.url) { if (error.request.url && error.request.url.trim() !== '') { diff --git a/apps/sim/blocks/blocks/agent.ts b/apps/sim/blocks/blocks/agent.ts index 427f18444..0972842f8 100644 --- a/apps/sim/blocks/blocks/agent.ts +++ b/apps/sim/blocks/blocks/agent.ts @@ -128,6 +128,7 @@ Write naturally and comprehensively based on what the user actually asks for.`, type: 'combobox', layout: 'half', placeholder: 'Type or select a model...', + required: true, options: () => { const ollamaModels = useOllamaStore.getState().models const baseModels = Object.keys(getBaseModelProviders()) @@ -191,6 +192,7 @@ Write naturally and comprehensively based on what the user actually asks for.`, placeholder: 'Enter your API key', password: true, connectionDroppable: false, + required: true, // Hide API key for all hosted models when running on hosted version condition: isHosted ? { @@ -401,18 +403,16 @@ Example 3 (Array Input): }, }, inputs: { - systemPrompt: { type: 'string', required: false }, - userPrompt: { type: 'string', required: false }, - memories: { type: 'json', required: false }, - model: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, - azureEndpoint: { type: 'string', required: false }, - azureApiVersion: { type: 'string', required: false }, + systemPrompt: { type: 'string', description: 'Initial system instructions' }, + userPrompt: { type: 'string', description: 'User message or context' }, + memories: { type: 'json', description: 'Agent memory data' }, + model: { type: 'string', description: 'AI model to use' }, + apiKey: { type: 'string', description: 'Provider API key' }, + azureEndpoint: { type: 'string', description: 'Azure OpenAI endpoint URL' }, + azureApiVersion: { type: 'string', description: 'Azure API version' }, responseFormat: { type: 'json', - required: false, - description: - 'Define the expected response format using JSON Schema. If not provided, returns plain text content.', + description: 'JSON response format schema', schema: { type: 'object', properties: { @@ -454,13 +454,13 @@ Example 3 (Array Input): required: ['schema'], }, }, - temperature: { type: 'number', required: false }, - tools: { type: 'json', required: false }, + temperature: { type: 'number', description: 'Response randomness level' }, + tools: { type: 'json', description: 'Available tools configuration' }, }, outputs: { - content: 'string', - model: 'string', - tokens: 'any', - toolCalls: 'any', + content: { type: 'string', description: 'Generated response content' }, + model: { type: 'string', description: 'Model used for generation' }, + tokens: { type: 'any', description: 'Token usage statistics' }, + toolCalls: { type: 'any', description: 'Tool calls made' }, }, } diff --git a/apps/sim/blocks/blocks/airtable.ts b/apps/sim/blocks/blocks/airtable.ts index 108be074c..7810e60e3 100644 --- a/apps/sim/blocks/blocks/airtable.ts +++ b/apps/sim/blocks/blocks/airtable.ts @@ -26,6 +26,7 @@ export const AirtableBlock: BlockConfig = { { label: 'Create Records', id: 'create' }, { label: 'Update Record', id: 'update' }, ], + value: () => 'list', }, { id: 'credential', @@ -36,6 +37,7 @@ export const AirtableBlock: BlockConfig = { serviceId: 'airtable', requiredScopes: ['data.records:read', 'data.records:write'], // Keep both scopes placeholder: 'Select Airtable account', + required: true, }, { id: 'baseId', @@ -43,6 +45,7 @@ export const AirtableBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter your base ID (e.g., appXXXXXXXXXXXXXX)', + required: true, }, { id: 'tableId', @@ -50,6 +53,7 @@ export const AirtableBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter table ID (e.g., tblXXXXXXXXXXXXXX)', + required: true, }, { id: 'recordId', @@ -58,6 +62,7 @@ export const AirtableBlock: BlockConfig = { layout: 'full', placeholder: 'ID of the record (e.g., recXXXXXXXXXXXXXX)', condition: { field: 'operation', value: ['get', 'update'] }, + required: true, }, { id: 'maxRecords', @@ -82,6 +87,7 @@ export const AirtableBlock: BlockConfig = { layout: 'full', placeholder: 'For Create: `[{ "fields": { ... } }]`\n', condition: { field: 'operation', value: ['create', 'updateMultiple'] }, + required: true, }, { id: 'fields', @@ -90,6 +96,7 @@ export const AirtableBlock: BlockConfig = { layout: 'full', placeholder: 'Fields to update: `{ "Field Name": "New Value" }`', condition: { field: 'operation', value: 'update' }, + required: true, }, ], tools: { @@ -153,21 +160,21 @@ export const AirtableBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - credential: { type: 'string', required: true }, - baseId: { type: 'string', required: true }, - tableId: { type: 'string', required: true }, + operation: { type: 'string', description: 'Operation to perform' }, + credential: { type: 'string', description: 'Airtable access token' }, + baseId: { type: 'string', description: 'Airtable base identifier' }, + tableId: { type: 'string', description: 'Airtable table identifier' }, // Conditional inputs - recordId: { type: 'string', required: true }, // Required for get/update - maxRecords: { type: 'number', required: false }, // Optional for list - filterFormula: { type: 'string', required: false }, // Optional for list - records: { type: 'json', required: false }, // Required for create/updateMultiple - fields: { type: 'json', required: false }, // Required for update single + recordId: { type: 'string', description: 'Record identifier' }, // Required for get/update + maxRecords: { type: 'number', description: 'Maximum records to return' }, // Optional for list + filterFormula: { type: 'string', description: 'Filter formula expression' }, // Optional for list + records: { type: 'json', description: 'Record data array' }, // Required for create/updateMultiple + fields: { type: 'json', description: 'Field data object' }, // Required for update single }, // Output structure depends on the operation, covered by AirtableResponse union type outputs: { - records: 'json', // Optional: for list, create, updateMultiple - record: 'json', // Optional: for get, update single - metadata: 'json', // Required: present in all responses + records: { type: 'json', description: 'Retrieved record data' }, // Optional: for list, create, updateMultiple + record: { type: 'json', description: 'Single record data' }, // Optional: for get, update single + metadata: { type: 'json', description: 'Operation metadata' }, // Required: present in all responses }, } diff --git a/apps/sim/blocks/blocks/api.ts b/apps/sim/blocks/blocks/api.ts index 8241df841..e4a297878 100644 --- a/apps/sim/blocks/blocks/api.ts +++ b/apps/sim/blocks/blocks/api.ts @@ -19,12 +19,14 @@ export const ApiBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter URL', + required: true, }, { id: 'method', title: 'Method', type: 'dropdown', layout: 'half', + required: true, options: [ { label: 'GET', id: 'GET' }, { label: 'POST', id: 'POST' }, @@ -85,15 +87,15 @@ Example: access: ['http_request'], }, inputs: { - url: { type: 'string', required: true }, - method: { type: 'string', required: true }, - headers: { type: 'json', required: false }, - body: { type: 'json', required: false }, - params: { type: 'json', required: false }, + url: { type: 'string', description: 'Request URL' }, + method: { type: 'string', description: 'HTTP method' }, + headers: { type: 'json', description: 'Request headers' }, + body: { type: 'json', description: 'Request body data' }, + params: { type: 'json', description: 'URL query parameters' }, }, outputs: { - data: 'any', - status: 'number', - headers: 'json', + data: { type: 'any', description: 'Response data' }, + status: { type: 'number', description: 'HTTP status code' }, + headers: { type: 'json', description: 'Response headers' }, }, } diff --git a/apps/sim/blocks/blocks/arxiv.ts b/apps/sim/blocks/blocks/arxiv.ts index 2c4c056a0..ded9b4bed 100644 --- a/apps/sim/blocks/blocks/arxiv.ts +++ b/apps/sim/blocks/blocks/arxiv.ts @@ -27,12 +27,13 @@ export const ArxivBlock: BlockConfig = { }, // Search operation inputs { - id: 'query', + id: 'searchQuery', title: 'Search Query', type: 'long-input', layout: 'full', placeholder: 'Enter search terms (e.g., "machine learning", "quantum physics")...', condition: { field: 'operation', value: 'arxiv_search' }, + required: true, }, { id: 'searchField', @@ -93,6 +94,7 @@ export const ArxivBlock: BlockConfig = { layout: 'full', placeholder: 'Enter ArXiv paper ID (e.g., 1706.03762, cs.AI/0001001)', condition: { field: 'operation', value: 'arxiv_get_paper' }, + required: true, }, // Get Author Papers operation inputs { @@ -102,6 +104,7 @@ export const ArxivBlock: BlockConfig = { layout: 'full', placeholder: 'Enter author name (e.g., "John Smith")...', condition: { field: 'operation', value: 'arxiv_get_author_papers' }, + required: true, }, { id: 'maxResults', @@ -135,25 +138,25 @@ export const ArxivBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, + operation: { type: 'string', description: 'Operation to perform' }, // Search operation - query: { type: 'string', required: false }, - searchField: { type: 'string', required: false }, - maxResults: { type: 'number', required: false }, - sortBy: { type: 'string', required: false }, - sortOrder: { type: 'string', required: false }, + searchQuery: { type: 'string', description: 'Search terms' }, + searchField: { type: 'string', description: 'Field to search in' }, + maxResults: { type: 'number', description: 'Maximum results to return' }, + sortBy: { type: 'string', description: 'Sort results by' }, + sortOrder: { type: 'string', description: 'Sort order direction' }, // Get Paper Details operation - paperId: { type: 'string', required: false }, + paperId: { type: 'string', description: 'ArXiv paper identifier' }, // Get Author Papers operation - authorName: { type: 'string', required: false }, + authorName: { type: 'string', description: 'Author name' }, }, outputs: { // Search output - papers: 'json', - totalResults: 'number', + papers: { type: 'json', description: 'Found papers data' }, + totalResults: { type: 'number', description: 'Total results count' }, // Get Paper Details output - paper: 'json', + paper: { type: 'json', description: 'Paper details' }, // Get Author Papers output - authorPapers: 'json', + authorPapers: { type: 'json', description: 'Author papers list' }, }, } diff --git a/apps/sim/blocks/blocks/browser_use.ts b/apps/sim/blocks/blocks/browser_use.ts index 08df64590..967c80f3d 100644 --- a/apps/sim/blocks/blocks/browser_use.ts +++ b/apps/sim/blocks/blocks/browser_use.ts @@ -19,6 +19,7 @@ export const BrowserUseBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'Describe what the browser agent should do...', + required: true, }, { id: 'variables', @@ -54,22 +55,23 @@ export const BrowserUseBlock: BlockConfig = { layout: 'full', password: true, placeholder: 'Enter your BrowserUse API key', + required: true, }, ], tools: { access: ['browser_use_run_task'], }, inputs: { - task: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, - variables: { type: 'json', required: false }, - model: { type: 'string', required: false }, - save_browser_data: { type: 'boolean', required: false }, + task: { type: 'string', description: 'Browser automation task' }, + apiKey: { type: 'string', description: 'BrowserUse API key' }, + variables: { type: 'json', description: 'Task variables' }, + model: { type: 'string', description: 'AI model to use' }, + save_browser_data: { type: 'boolean', description: 'Save browser data' }, }, outputs: { - id: 'string', - success: 'boolean', - output: 'any', - steps: 'json', + id: { type: 'string', description: 'Task execution identifier' }, + success: { type: 'boolean', description: 'Task completion status' }, + output: { type: 'any', description: 'Task output data' }, + steps: { type: 'json', description: 'Execution steps taken' }, }, } diff --git a/apps/sim/blocks/blocks/clay.ts b/apps/sim/blocks/blocks/clay.ts index 07d2e13e4..6272d3da1 100644 --- a/apps/sim/blocks/blocks/clay.ts +++ b/apps/sim/blocks/blocks/clay.ts @@ -19,6 +19,7 @@ export const ClayBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter Clay webhook URL', + required: true, }, { id: 'data', @@ -26,6 +27,7 @@ export const ClayBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'Enter your JSON data to populate your Clay table', + required: true, description: `JSON vs. Plain Text: JSON: Best for populating multiple columns. Plain Text: Best for populating a table in free-form style. @@ -39,17 +41,18 @@ Plain Text: Best for populating a table in free-form style. placeholder: 'Enter your Clay Auth token', password: true, connectionDroppable: false, + required: true, }, ], tools: { access: ['clay_populate'], }, inputs: { - authToken: { type: 'string', required: true }, - webhookURL: { type: 'string', required: true }, - data: { type: 'json', required: true }, + authToken: { type: 'string', description: 'Clay authentication token' }, + webhookURL: { type: 'string', description: 'Clay webhook URL' }, + data: { type: 'json', description: 'Data to populate' }, }, outputs: { - data: 'any', + data: { type: 'any', description: 'Response data' }, }, } diff --git a/apps/sim/blocks/blocks/condition.ts b/apps/sim/blocks/blocks/condition.ts index ff2cd2e91..4d06e05a1 100644 --- a/apps/sim/blocks/blocks/condition.ts +++ b/apps/sim/blocks/blocks/condition.ts @@ -37,9 +37,9 @@ export const ConditionBlock: BlockConfig = { }, inputs: {}, outputs: { - content: 'string', - conditionResult: 'boolean', - selectedPath: 'json', - selectedConditionId: 'string', + content: { type: 'string', description: 'Condition evaluation content' }, + conditionResult: { type: 'boolean', description: 'Condition result' }, + selectedPath: { type: 'json', description: 'Selected execution path' }, + selectedConditionId: { type: 'string', description: 'Selected condition identifier' }, }, } diff --git a/apps/sim/blocks/blocks/confluence.ts b/apps/sim/blocks/blocks/confluence.ts index e45df3ffe..076b994b3 100644 --- a/apps/sim/blocks/blocks/confluence.ts +++ b/apps/sim/blocks/blocks/confluence.ts @@ -23,6 +23,7 @@ export const ConfluenceBlock: BlockConfig = { { label: 'Read Page', id: 'read' }, { label: 'Update Page', id: 'update' }, ], + value: () => 'read', }, { id: 'domain', @@ -30,6 +31,7 @@ export const ConfluenceBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter Confluence domain (e.g., simstudio.atlassian.net)', + required: true, }, { id: 'credential', @@ -45,6 +47,7 @@ export const ConfluenceBlock: BlockConfig = { 'offline_access', ], placeholder: 'Select Confluence account', + required: true, }, // Page selector (basic mode) { @@ -116,20 +119,20 @@ export const ConfluenceBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - domain: { type: 'string', required: true }, - credential: { type: 'string', required: true }, - pageId: { type: 'string', required: false }, - manualPageId: { type: 'string', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + domain: { type: 'string', description: 'Confluence domain' }, + credential: { type: 'string', description: 'Confluence access token' }, + pageId: { type: 'string', description: 'Page identifier' }, + manualPageId: { type: 'string', description: 'Manual page identifier' }, // Update operation inputs - title: { type: 'string', required: false }, - content: { type: 'string', required: false }, + title: { type: 'string', description: 'New page title' }, + content: { type: 'string', description: 'New page content' }, }, outputs: { - ts: 'string', - pageId: 'string', - content: 'string', - title: 'string', - success: 'boolean', + ts: { type: 'string', description: 'Timestamp' }, + pageId: { type: 'string', description: 'Page identifier' }, + content: { type: 'string', description: 'Page content' }, + title: { type: 'string', description: 'Page title' }, + success: { type: 'boolean', description: 'Operation success status' }, }, } diff --git a/apps/sim/blocks/blocks/discord.ts b/apps/sim/blocks/blocks/discord.ts index b672c960e..fcc02f008 100644 --- a/apps/sim/blocks/blocks/discord.ts +++ b/apps/sim/blocks/blocks/discord.ts @@ -23,6 +23,7 @@ export const DiscordBlock: BlockConfig = { { label: 'Get Server Information', id: 'discord_get_server' }, { label: 'Get User Information', id: 'discord_get_user' }, ], + value: () => 'discord_send_message', }, { id: 'botToken', @@ -31,6 +32,7 @@ export const DiscordBlock: BlockConfig = { layout: 'full', placeholder: 'Enter Discord bot token', password: true, + required: true, }, // Server selector (basic mode) { @@ -198,18 +200,18 @@ export const DiscordBlock: BlockConfig = { }, }, inputs: { - 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 }, + operation: { type: 'string', description: 'Operation to perform' }, + botToken: { type: 'string', description: 'Discord bot token' }, + serverId: { type: 'string', description: 'Discord server identifier' }, + manualServerId: { type: 'string', description: 'Manual server identifier' }, + channelId: { type: 'string', description: 'Discord channel identifier' }, + manualChannelId: { type: 'string', description: 'Manual channel identifier' }, + content: { type: 'string', description: 'Message content' }, + limit: { type: 'number', description: 'Message limit' }, + userId: { type: 'string', description: 'Discord user identifier' }, }, outputs: { - message: 'string', - data: 'any', + message: { type: 'string', description: 'Message content' }, + data: { type: 'any', description: 'Response data' }, }, } diff --git a/apps/sim/blocks/blocks/elevenlabs.ts b/apps/sim/blocks/blocks/elevenlabs.ts index 2e326cb36..d5198d781 100644 --- a/apps/sim/blocks/blocks/elevenlabs.ts +++ b/apps/sim/blocks/blocks/elevenlabs.ts @@ -12,30 +12,6 @@ export const ElevenLabsBlock: BlockConfig = { bgColor: '#181C1E', icon: ElevenLabsIcon, - tools: { - access: ['elevenlabs_tts'], - config: { - tool: () => 'elevenlabs_tts', - params: (params) => ({ - apiKey: params.apiKey, - text: params.text, - voiceId: params.voiceId, - modelId: params.modelId, - }), - }, - }, - - inputs: { - text: { type: 'string', required: true }, - voiceId: { type: 'string', required: true }, - modelId: { type: 'string', required: false }, - apiKey: { type: 'string', required: true }, - }, - - outputs: { - audioUrl: 'string', - }, - subBlocks: [ { id: 'text', @@ -43,6 +19,7 @@ export const ElevenLabsBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'Enter the text to convert to speech', + required: true, }, { id: 'voiceId', @@ -50,6 +27,7 @@ export const ElevenLabsBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter the voice ID', + required: true, }, { id: 'modelId', @@ -71,6 +49,31 @@ export const ElevenLabsBlock: BlockConfig = { layout: 'full', placeholder: 'Enter your ElevenLabs API key', password: true, + required: true, }, ], + + tools: { + access: ['elevenlabs_tts'], + config: { + tool: () => 'elevenlabs_tts', + params: (params) => ({ + apiKey: params.apiKey, + text: params.text, + voiceId: params.voiceId, + modelId: params.modelId, + }), + }, + }, + + inputs: { + text: { type: 'string', description: 'Text to convert' }, + voiceId: { type: 'string', description: 'Voice identifier' }, + modelId: { type: 'string', description: 'Model identifier' }, + apiKey: { type: 'string', description: 'ElevenLabs API key' }, + }, + + outputs: { + audioUrl: { type: 'string', description: 'Generated audio URL' }, + }, } diff --git a/apps/sim/blocks/blocks/evaluator.ts b/apps/sim/blocks/blocks/evaluator.ts index 8589a258f..692d0c0b1 100644 --- a/apps/sim/blocks/blocks/evaluator.ts +++ b/apps/sim/blocks/blocks/evaluator.ts @@ -160,6 +160,7 @@ export const EvaluatorBlock: BlockConfig = { title: 'Evaluation Metrics', type: 'eval-input', layout: 'full', + required: true, }, { id: 'content', @@ -167,12 +168,14 @@ export const EvaluatorBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter the content to evaluate', + required: true, }, { id: 'model', title: 'Model', type: 'dropdown', layout: 'half', + required: true, options: () => { const ollamaModels = useOllamaStore.getState().models const baseModels = Object.keys(getBaseModelProviders()) @@ -190,6 +193,7 @@ export const EvaluatorBlock: BlockConfig = { placeholder: 'Enter your API key', password: true, connectionDroppable: false, + required: true, condition: isHosted ? { field: 'model', @@ -270,8 +274,7 @@ export const EvaluatorBlock: BlockConfig = { inputs: { metrics: { type: 'json' as ParamType, - required: true, - description: 'Array of metrics to evaluate against', + description: 'Evaluation metrics configuration', schema: { type: 'array', properties: {}, @@ -305,14 +308,14 @@ export const EvaluatorBlock: BlockConfig = { }, }, }, - model: { type: 'string' as ParamType, required: true }, - apiKey: { type: 'string' as ParamType, required: true }, - content: { type: 'string' as ParamType, required: true }, + model: { type: 'string' as ParamType, description: 'AI model to use' }, + apiKey: { type: 'string' as ParamType, description: 'Provider API key' }, + content: { type: 'string' as ParamType, description: 'Content to evaluate' }, }, outputs: { - content: 'string', - model: 'string', - tokens: 'any', - cost: 'any', + content: { type: 'string', description: 'Evaluation results' }, + model: { type: 'string', description: 'Model used' }, + tokens: { type: 'any', description: 'Token usage' }, + cost: { type: 'any', description: 'Cost information' }, } as any, } diff --git a/apps/sim/blocks/blocks/exa.ts b/apps/sim/blocks/blocks/exa.ts index c9c489809..2e8004e12 100644 --- a/apps/sim/blocks/blocks/exa.ts +++ b/apps/sim/blocks/blocks/exa.ts @@ -35,6 +35,7 @@ export const ExaBlock: BlockConfig = { layout: 'full', placeholder: 'Enter your search query...', condition: { field: 'operation', value: 'exa_search' }, + required: true, }, { id: 'numResults', @@ -60,7 +61,7 @@ export const ExaBlock: BlockConfig = { { label: 'Auto', id: 'auto' }, { label: 'Neural', id: 'neural' }, { label: 'Keyword', id: 'keyword' }, - { label: 'Magic', id: 'magic' }, + { label: 'Fast', id: 'fast' }, ], value: () => 'auto', condition: { field: 'operation', value: 'exa_search' }, @@ -73,6 +74,7 @@ export const ExaBlock: BlockConfig = { layout: 'full', placeholder: 'Enter URLs to retrieve content from (comma-separated)...', condition: { field: 'operation', value: 'exa_get_contents' }, + required: true, }, { id: 'text', @@ -97,6 +99,7 @@ export const ExaBlock: BlockConfig = { layout: 'full', placeholder: 'Enter URL to find similar links for...', condition: { field: 'operation', value: 'exa_find_similar_links' }, + required: true, }, { id: 'numResults', @@ -121,6 +124,7 @@ export const ExaBlock: BlockConfig = { layout: 'full', placeholder: 'Enter your question...', condition: { field: 'operation', value: 'exa_answer' }, + required: true, }, { id: 'text', @@ -137,6 +141,7 @@ export const ExaBlock: BlockConfig = { layout: 'full', placeholder: 'Enter your research topic or question...', condition: { field: 'operation', value: 'exa_research' }, + required: true, }, { id: 'includeText', @@ -153,6 +158,7 @@ export const ExaBlock: BlockConfig = { layout: 'full', placeholder: 'Enter your Exa API key', password: true, + required: true, }, ], tools: { @@ -188,29 +194,29 @@ export const ExaBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, + operation: { type: 'string', description: 'Operation to perform' }, + apiKey: { type: 'string', description: 'Exa API key' }, // Search operation - query: { type: 'string', required: false }, - numResults: { type: 'number', required: false }, - useAutoprompt: { type: 'boolean', required: false }, - type: { type: 'string', required: false }, + query: { type: 'string', description: 'Search query terms' }, + numResults: { type: 'number', description: 'Number of results' }, + useAutoprompt: { type: 'boolean', description: 'Use autoprompt feature' }, + type: { type: 'string', description: 'Search type' }, // Get Contents operation - urls: { type: 'string', required: false }, - text: { type: 'boolean', required: false }, - summaryQuery: { type: 'string', required: false }, + urls: { type: 'string', description: 'URLs to retrieve' }, + text: { type: 'boolean', description: 'Include text content' }, + summaryQuery: { type: 'string', description: 'Summary query guidance' }, // Find Similar Links operation - url: { type: 'string', required: false }, + url: { type: 'string', description: 'Source URL' }, }, outputs: { // Search output - results: 'json', + results: { type: 'json', description: 'Search results' }, // Find Similar Links output - similarLinks: 'json', + similarLinks: { type: 'json', description: 'Similar links found' }, // Answer output - answer: 'string', - citations: 'json', + answer: { type: 'string', description: 'Generated answer' }, + citations: { type: 'json', description: 'Answer citations' }, // Research output - research: 'json', + research: { type: 'json', description: 'Research findings' }, }, } diff --git a/apps/sim/blocks/blocks/file.ts b/apps/sim/blocks/blocks/file.ts index 393157a83..431688da0 100644 --- a/apps/sim/blocks/blocks/file.ts +++ b/apps/sim/blocks/blocks/file.ts @@ -122,16 +122,16 @@ export const FileBlock: BlockConfig = { // Conditionally require inputMethod and filePath only if URL input is enabled ...(shouldEnableURLInput ? { - inputMethod: { type: 'string', required: false }, // Not strictly required as it defaults - filePath: { type: 'string', required: false }, // Required only if inputMethod is 'url' (validated in params) + inputMethod: { type: 'string', description: 'Input method selection' }, // Not strictly required as it defaults + filePath: { type: 'string', description: 'File URL path' }, // Required only if inputMethod is 'url' (validated in params) } : {}), - fileType: { type: 'string', required: false }, + fileType: { type: 'string', description: 'File type' }, // File input is always potentially needed, but only required if method is 'upload' (validated in params) - file: { type: 'json', required: false }, + file: { type: 'json', description: 'Uploaded file data' }, }, outputs: { - files: 'json', - combinedContent: 'string', + files: { type: 'json', description: 'Parsed file data' }, + combinedContent: { type: 'string', description: 'Combined file content' }, }, } diff --git a/apps/sim/blocks/blocks/firecrawl.ts b/apps/sim/blocks/blocks/firecrawl.ts index e3459223e..bb9e4427b 100644 --- a/apps/sim/blocks/blocks/firecrawl.ts +++ b/apps/sim/blocks/blocks/firecrawl.ts @@ -35,6 +35,7 @@ export const FirecrawlBlock: BlockConfig = { field: 'operation', value: ['scrape', 'crawl'], }, + required: true, }, { id: 'onlyMainContent', @@ -67,6 +68,7 @@ export const FirecrawlBlock: BlockConfig = { field: 'operation', value: 'search', }, + required: true, }, { id: 'apiKey', @@ -75,6 +77,7 @@ export const FirecrawlBlock: BlockConfig = { layout: 'full', placeholder: 'Enter your Firecrawl API key', password: true, + required: true, }, ], tools: { @@ -108,24 +111,24 @@ export const FirecrawlBlock: BlockConfig = { }, }, inputs: { - apiKey: { type: 'string', required: true }, - operation: { type: 'string', required: true }, - url: { type: 'string', required: false }, - limit: { type: 'string', required: false }, - query: { type: 'string', required: false }, - scrapeOptions: { type: 'json', required: false }, + apiKey: { type: 'string', description: 'Firecrawl API key' }, + operation: { type: 'string', description: 'Operation to perform' }, + url: { type: 'string', description: 'Target website URL' }, + limit: { type: 'string', description: 'Page crawl limit' }, + query: { type: 'string', description: 'Search query terms' }, + scrapeOptions: { type: 'json', description: 'Scraping options' }, }, outputs: { // Scrape output - markdown: 'string', - html: 'any', - metadata: 'json', + markdown: { type: 'string', description: 'Page content markdown' }, + html: { type: 'any', description: 'Raw HTML content' }, + metadata: { type: 'json', description: 'Page metadata' }, // Search output - data: 'json', - warning: 'any', + data: { type: 'json', description: 'Search results data' }, + warning: { type: 'any', description: 'Warning messages' }, // Crawl output - pages: 'json', - total: 'number', - creditsUsed: 'number', + pages: { type: 'json', description: 'Crawled pages data' }, + total: { type: 'number', description: 'Total pages found' }, + creditsUsed: { type: 'number', description: 'Credits consumed' }, }, } diff --git a/apps/sim/blocks/blocks/function.ts b/apps/sim/blocks/blocks/function.ts index f1f42dfcc..500042461 100644 --- a/apps/sim/blocks/blocks/function.ts +++ b/apps/sim/blocks/blocks/function.ts @@ -76,11 +76,11 @@ try { access: ['function_execute'], }, inputs: { - code: { type: 'string', required: false }, - timeout: { type: 'number', required: false }, + code: { type: 'string', description: 'JavaScript/TypeScript code to execute' }, + timeout: { type: 'number', description: 'Execution timeout' }, }, outputs: { - result: 'any', - stdout: 'string', + result: { type: 'any', description: 'Execution result' }, + stdout: { type: 'string', description: 'Console output' }, }, } diff --git a/apps/sim/blocks/blocks/github.ts b/apps/sim/blocks/blocks/github.ts index fe3f822f7..8900d8a6a 100644 --- a/apps/sim/blocks/blocks/github.ts +++ b/apps/sim/blocks/blocks/github.ts @@ -32,6 +32,7 @@ export const GitHubBlock: BlockConfig = { type: 'short-input', layout: 'half', placeholder: 'e.g., microsoft', + required: true, }, { id: 'repo', @@ -39,6 +40,7 @@ export const GitHubBlock: BlockConfig = { type: 'short-input', layout: 'half', placeholder: 'e.g., vscode', + required: true, }, { id: 'pullNumber', @@ -47,6 +49,7 @@ export const GitHubBlock: BlockConfig = { layout: 'half', placeholder: 'e.g., 123', condition: { field: 'operation', value: 'github_pr' }, + required: true, }, { id: 'body', @@ -55,6 +58,7 @@ export const GitHubBlock: BlockConfig = { layout: 'full', placeholder: 'Enter comment text', condition: { field: 'operation', value: 'github_comment' }, + required: true, }, { id: 'pullNumber', @@ -63,6 +67,7 @@ export const GitHubBlock: BlockConfig = { layout: 'half', placeholder: 'e.g., 123', condition: { field: 'operation', value: 'github_comment' }, + required: true, }, { id: 'branch', @@ -79,6 +84,7 @@ export const GitHubBlock: BlockConfig = { layout: 'full', placeholder: 'Enter GitHub Token', password: true, + required: true, }, { id: 'commentType', @@ -142,21 +148,21 @@ export const GitHubBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - owner: { type: 'string', required: true }, - repo: { type: 'string', required: true }, - pullNumber: { type: 'number', required: false }, - body: { type: 'string', required: false }, - apiKey: { type: 'string', required: true }, - commentType: { type: 'string', required: false }, - path: { type: 'string', required: false }, - line: { type: 'number', required: false }, - side: { type: 'string', required: false }, - commitId: { type: 'string', required: false }, - branch: { type: 'string', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + owner: { type: 'string', description: 'Repository owner' }, + repo: { type: 'string', description: 'Repository name' }, + pullNumber: { type: 'number', description: 'Pull request number' }, + body: { type: 'string', description: 'Comment text' }, + apiKey: { type: 'string', description: 'GitHub access token' }, + commentType: { type: 'string', description: 'Comment type' }, + path: { type: 'string', description: 'File path' }, + line: { type: 'number', description: 'Line number' }, + side: { type: 'string', description: 'Comment side' }, + commitId: { type: 'string', description: 'Commit identifier' }, + branch: { type: 'string', description: 'Branch name' }, }, outputs: { - content: 'string', - metadata: 'json', + content: { type: 'string', description: 'Response content' }, + metadata: { type: 'json', description: 'Response metadata' }, }, } diff --git a/apps/sim/blocks/blocks/gmail.ts b/apps/sim/blocks/blocks/gmail.ts index f7ef66a7e..ab0db99d3 100644 --- a/apps/sim/blocks/blocks/gmail.ts +++ b/apps/sim/blocks/blocks/gmail.ts @@ -25,6 +25,7 @@ export const GmailBlock: BlockConfig = { { label: 'Draft Email', id: 'draft_gmail' }, { label: 'Search Email', id: 'search_gmail' }, ], + value: () => 'send_gmail', }, // Gmail Credentials { @@ -41,6 +42,7 @@ export const GmailBlock: BlockConfig = { 'https://www.googleapis.com/auth/gmail.labels', ], placeholder: 'Select Gmail account', + required: true, }, // Send Email Fields { @@ -50,6 +52,7 @@ export const GmailBlock: BlockConfig = { layout: 'full', placeholder: 'Recipient email address', condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] }, + required: true, }, { id: 'subject', @@ -58,6 +61,7 @@ export const GmailBlock: BlockConfig = { layout: 'full', placeholder: 'Email subject', condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] }, + required: true, }, { id: 'body', @@ -66,6 +70,7 @@ export const GmailBlock: BlockConfig = { layout: 'full', placeholder: 'Email content', condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] }, + required: true, }, // Label/folder selector (basic mode) { @@ -123,6 +128,7 @@ export const GmailBlock: BlockConfig = { layout: 'full', placeholder: 'Enter search terms', condition: { field: 'operation', value: 'search_gmail' }, + required: true, }, { id: 'maxResults', @@ -170,23 +176,23 @@ export const GmailBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - credential: { type: 'string', required: true }, + operation: { type: 'string', description: 'Operation to perform' }, + credential: { type: 'string', description: 'Gmail access token' }, // Send operation inputs - to: { type: 'string', required: false }, - subject: { type: 'string', required: false }, - body: { type: 'string', required: false }, + to: { type: 'string', description: 'Recipient email address' }, + subject: { type: 'string', description: 'Email subject' }, + body: { type: 'string', description: 'Email content' }, // Read operation inputs - folder: { type: 'string', required: false }, - manualFolder: { type: 'string', required: false }, - messageId: { type: 'string', required: false }, - unreadOnly: { type: 'boolean', required: false }, + folder: { type: 'string', description: 'Gmail folder' }, + manualFolder: { type: 'string', description: 'Manual folder name' }, + messageId: { type: 'string', description: 'Message identifier' }, + unreadOnly: { type: 'boolean', description: 'Unread messages only' }, // Search operation inputs - query: { type: 'string', required: false }, - maxResults: { type: 'number', required: false }, + query: { type: 'string', description: 'Search query' }, + maxResults: { type: 'number', description: 'Maximum results' }, }, outputs: { - content: 'string', - metadata: 'json', + content: { type: 'string', description: 'Response content' }, + metadata: { type: 'json', description: 'Email metadata' }, }, } diff --git a/apps/sim/blocks/blocks/google.ts b/apps/sim/blocks/blocks/google.ts index 2429cde42..ebdc48c2f 100644 --- a/apps/sim/blocks/blocks/google.ts +++ b/apps/sim/blocks/blocks/google.ts @@ -20,6 +20,7 @@ export const GoogleSearchBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'Enter your search query', + required: true, }, { id: 'searchEngineId', @@ -27,7 +28,7 @@ export const GoogleSearchBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter your Custom Search Engine ID', - description: 'Required Custom Search Engine ID', + required: true, }, { id: 'apiKey', @@ -35,8 +36,8 @@ export const GoogleSearchBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter your Google API key', - description: 'Required API Key for Google Search', password: true, + required: true, }, { id: 'num', @@ -44,7 +45,7 @@ export const GoogleSearchBlock: BlockConfig = { type: 'short-input', layout: 'half', placeholder: '10', - description: 'Number of search results to return (max: 10)', + required: true, }, ], @@ -62,14 +63,14 @@ export const GoogleSearchBlock: BlockConfig = { }, inputs: { - query: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, - searchEngineId: { type: 'string', required: true }, - num: { type: 'string', required: false }, + query: { type: 'string', description: 'Search query terms' }, + apiKey: { type: 'string', description: 'Google API key' }, + searchEngineId: { type: 'string', description: 'Custom search engine ID' }, + num: { type: 'string', description: 'Number of results' }, }, outputs: { - items: 'json', - searchInformation: 'json', + items: { type: 'json', description: 'Search result items' }, + searchInformation: { type: 'json', description: 'Search metadata' }, }, } diff --git a/apps/sim/blocks/blocks/google_calendar.ts b/apps/sim/blocks/blocks/google_calendar.ts index 9143e8dcf..46cc436c8 100644 --- a/apps/sim/blocks/blocks/google_calendar.ts +++ b/apps/sim/blocks/blocks/google_calendar.ts @@ -25,12 +25,14 @@ export const GoogleCalendarBlock: BlockConfig = { { label: 'Quick Add (Natural Language)', id: 'quick_add' }, { label: 'Invite Attendees', id: 'invite' }, ], + value: () => 'create', }, { id: 'credential', title: 'Google Calendar Account', type: 'oauth-input', layout: 'full', + required: true, provider: 'google-calendar', serviceId: 'google-calendar', requiredScopes: ['https://www.googleapis.com/auth/calendar'], @@ -66,6 +68,7 @@ export const GoogleCalendarBlock: BlockConfig = { layout: 'full', placeholder: 'Meeting with team', condition: { field: 'operation', value: 'create' }, + required: true, }, { id: 'description', @@ -90,6 +93,7 @@ export const GoogleCalendarBlock: BlockConfig = { layout: 'half', placeholder: '2025-06-03T10:00:00-08:00', condition: { field: 'operation', value: 'create' }, + required: true, }, { id: 'endDateTime', @@ -98,6 +102,7 @@ export const GoogleCalendarBlock: BlockConfig = { layout: 'half', placeholder: '2025-06-03T11:00:00-08:00', condition: { field: 'operation', value: 'create' }, + required: true, }, { id: 'attendees', @@ -134,6 +139,7 @@ export const GoogleCalendarBlock: BlockConfig = { layout: 'full', placeholder: 'Event ID', condition: { field: 'operation', value: ['get', 'invite'] }, + required: true, }, // Invite Attendees Fields @@ -165,6 +171,7 @@ export const GoogleCalendarBlock: BlockConfig = { layout: 'full', placeholder: 'Meeting with John tomorrow at 3pm for 1 hour', condition: { field: 'operation', value: 'quick_add' }, + required: true, }, { id: 'attendees', @@ -173,6 +180,7 @@ export const GoogleCalendarBlock: BlockConfig = { layout: 'full', placeholder: 'john@example.com, jane@example.com', condition: { field: 'operation', value: 'quick_add' }, + required: true, }, // Notification setting (for create, quick_add, invite) @@ -267,37 +275,37 @@ export const GoogleCalendarBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - credential: { type: 'string', required: true }, - calendarId: { type: 'string', required: false }, - manualCalendarId: { type: 'string', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + credential: { type: 'string', description: 'Google Calendar access token' }, + calendarId: { type: 'string', description: 'Calendar identifier' }, + manualCalendarId: { type: 'string', description: 'Manual calendar identifier' }, // Create operation inputs - summary: { type: 'string', required: false }, - description: { type: 'string', required: false }, - location: { type: 'string', required: false }, - startDateTime: { type: 'string', required: false }, - endDateTime: { type: 'string', required: false }, - attendees: { type: 'string', required: false }, + summary: { type: 'string', description: 'Event title' }, + description: { type: 'string', description: 'Event description' }, + location: { type: 'string', description: 'Event location' }, + startDateTime: { type: 'string', description: 'Event start time' }, + endDateTime: { type: 'string', description: 'Event end time' }, + attendees: { type: 'string', description: 'Attendee email list' }, // List operation inputs - timeMin: { type: 'string', required: false }, - timeMax: { type: 'string', required: false }, + timeMin: { type: 'string', description: 'Start time filter' }, + timeMax: { type: 'string', description: 'End time filter' }, // Get/Invite operation inputs - eventId: { type: 'string', required: false }, + eventId: { type: 'string', description: 'Event identifier' }, // Quick add inputs - text: { type: 'string', required: false }, + text: { type: 'string', description: 'Natural language event' }, // Invite specific inputs - replaceExisting: { type: 'string', required: false }, + replaceExisting: { type: 'string', description: 'Replace existing attendees' }, // Common inputs - sendUpdates: { type: 'string', required: false }, + sendUpdates: { type: 'string', description: 'Send email notifications' }, }, outputs: { - content: 'string', - metadata: 'json', + content: { type: 'string', description: 'Operation response content' }, + metadata: { type: 'json', description: 'Event metadata' }, }, } diff --git a/apps/sim/blocks/blocks/google_docs.ts b/apps/sim/blocks/blocks/google_docs.ts index b96eb5959..1e257b233 100644 --- a/apps/sim/blocks/blocks/google_docs.ts +++ b/apps/sim/blocks/blocks/google_docs.ts @@ -24,6 +24,7 @@ export const GoogleDocsBlock: BlockConfig = { { label: 'Write to Document', id: 'write' }, { label: 'Create Document', id: 'create' }, ], + value: () => 'read', }, // Google Docs Credentials { @@ -31,6 +32,7 @@ export const GoogleDocsBlock: BlockConfig = { title: 'Google Account', type: 'oauth-input', layout: 'full', + required: true, provider: 'google-docs', serviceId: 'google-docs', requiredScopes: ['https://www.googleapis.com/auth/drive.file'], @@ -68,6 +70,7 @@ export const GoogleDocsBlock: BlockConfig = { layout: 'full', placeholder: 'Enter title for the new document', condition: { field: 'operation', value: 'create' }, + required: true, }, // Folder selector (basic mode) { @@ -101,6 +104,7 @@ export const GoogleDocsBlock: BlockConfig = { layout: 'full', placeholder: 'Enter document content', condition: { field: 'operation', value: 'write' }, + required: true, }, // Content Field for create operation { @@ -144,18 +148,18 @@ export const GoogleDocsBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - credential: { type: 'string', required: true }, - documentId: { type: 'string', required: false }, - manualDocumentId: { type: 'string', required: false }, - title: { type: 'string', required: false }, - folderSelector: { type: 'string', required: false }, - folderId: { type: 'string', required: false }, - content: { type: 'string', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + credential: { type: 'string', description: 'Google Docs access token' }, + documentId: { type: 'string', description: 'Document identifier' }, + manualDocumentId: { type: 'string', description: 'Manual document identifier' }, + title: { type: 'string', description: 'Document title' }, + folderSelector: { type: 'string', description: 'Selected folder' }, + folderId: { type: 'string', description: 'Folder identifier' }, + content: { type: 'string', description: 'Document content' }, }, outputs: { - content: 'string', - metadata: 'json', - updatedContent: 'boolean', + content: { type: 'string', description: 'Document content' }, + metadata: { type: 'json', description: 'Document metadata' }, + updatedContent: { type: 'boolean', description: 'Content update status' }, }, } diff --git a/apps/sim/blocks/blocks/google_drive.ts b/apps/sim/blocks/blocks/google_drive.ts index d98195a3b..0454d0368 100644 --- a/apps/sim/blocks/blocks/google_drive.ts +++ b/apps/sim/blocks/blocks/google_drive.ts @@ -22,9 +22,9 @@ export const GoogleDriveBlock: BlockConfig = { options: [ { label: 'Create Folder', id: 'create_folder' }, { label: 'Upload File', id: 'upload' }, - // { label: 'Get File Content', id: 'get_content' }, { label: 'List Files', id: 'list' }, ], + value: () => 'create_folder', }, // Google Drive Credentials { @@ -32,6 +32,7 @@ export const GoogleDriveBlock: BlockConfig = { title: 'Google Drive Account', type: 'oauth-input', layout: 'full', + required: true, provider: 'google-drive', serviceId: 'google-drive', requiredScopes: ['https://www.googleapis.com/auth/drive.file'], @@ -45,6 +46,7 @@ export const GoogleDriveBlock: BlockConfig = { layout: 'full', placeholder: 'Name of the file', condition: { field: 'operation', value: 'upload' }, + required: true, }, { id: 'content', @@ -53,6 +55,7 @@ export const GoogleDriveBlock: BlockConfig = { layout: 'full', placeholder: 'Content to upload to the file', condition: { field: 'operation', value: 'upload' }, + required: true, }, { id: 'mimeType', @@ -139,6 +142,7 @@ export const GoogleDriveBlock: BlockConfig = { layout: 'full', placeholder: 'Name for the new folder', condition: { field: 'operation', value: 'create_folder' }, + required: true, }, { id: 'folderSelector', @@ -211,8 +215,6 @@ export const GoogleDriveBlock: BlockConfig = { switch (params.operation) { case 'upload': return 'google_drive_upload' - // case 'get_content': - // return 'google_drive_get_content' case 'create_folder': return 'google_drive_create_folder' case 'list': @@ -238,22 +240,20 @@ export const GoogleDriveBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - credential: { type: 'string', required: true }, + operation: { type: 'string', description: 'Operation to perform' }, + credential: { type: 'string', description: 'Google Drive access token' }, // Upload and Create Folder operation inputs - fileName: { type: 'string', required: false }, - content: { type: 'string', required: false }, - mimeType: { type: 'string', required: false }, - // Get Content operation inputs - // fileId: { type: 'string', required: false }, + fileName: { type: 'string', description: 'File or folder name' }, + content: { type: 'string', description: 'File content' }, + mimeType: { type: 'string', description: 'File MIME type' }, // List operation inputs - folderSelector: { type: 'string', required: false }, - manualFolderId: { type: 'string', required: false }, - query: { type: 'string', required: false }, - pageSize: { type: 'number', required: false }, + folderSelector: { type: 'string', description: 'Selected folder' }, + manualFolderId: { type: 'string', description: 'Manual folder identifier' }, + query: { type: 'string', description: 'Search query' }, + pageSize: { type: 'number', description: 'Results per page' }, }, outputs: { - file: 'json', - files: 'json', + file: { type: 'json', description: 'File data' }, + files: { type: 'json', description: 'Files list' }, }, } diff --git a/apps/sim/blocks/blocks/google_sheets.ts b/apps/sim/blocks/blocks/google_sheets.ts index 1a72a3a95..c23237024 100644 --- a/apps/sim/blocks/blocks/google_sheets.ts +++ b/apps/sim/blocks/blocks/google_sheets.ts @@ -25,6 +25,7 @@ export const GoogleSheetsBlock: BlockConfig = { { label: 'Update Data', id: 'update' }, { label: 'Append Data', id: 'append' }, ], + value: () => 'read', }, // Google Sheets Credentials { @@ -32,6 +33,7 @@ export const GoogleSheetsBlock: BlockConfig = { title: 'Google Account', type: 'oauth-input', layout: 'full', + required: true, provider: 'google-sheets', serviceId: 'google-sheets', requiredScopes: ['https://www.googleapis.com/auth/spreadsheets'], @@ -76,6 +78,7 @@ export const GoogleSheetsBlock: BlockConfig = { placeholder: 'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])', condition: { field: 'operation', value: 'write' }, + required: true, }, { id: 'valueInputOption', @@ -97,6 +100,7 @@ export const GoogleSheetsBlock: BlockConfig = { placeholder: 'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])', condition: { field: 'operation', value: 'update' }, + required: true, }, { id: 'valueInputOption', @@ -118,6 +122,7 @@ export const GoogleSheetsBlock: BlockConfig = { placeholder: 'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])', condition: { field: 'operation', value: 'append' }, + required: true, }, { id: 'valueInputOption', @@ -191,22 +196,22 @@ export const GoogleSheetsBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - credential: { type: 'string', required: true }, - spreadsheetId: { type: 'string', required: false }, - manualSpreadsheetId: { type: 'string', required: false }, - range: { type: 'string', required: false }, - values: { type: 'string', required: false }, - valueInputOption: { type: 'string', required: false }, - insertDataOption: { type: 'string', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + credential: { type: 'string', description: 'Google Sheets access token' }, + spreadsheetId: { type: 'string', description: 'Spreadsheet identifier' }, + manualSpreadsheetId: { type: 'string', description: 'Manual spreadsheet identifier' }, + range: { type: 'string', description: 'Cell range' }, + values: { type: 'string', description: 'Cell values data' }, + valueInputOption: { type: 'string', description: 'Value input option' }, + insertDataOption: { type: 'string', description: 'Data insertion option' }, }, outputs: { - data: 'json', - metadata: 'json', - updatedRange: 'string', - updatedRows: 'number', - updatedColumns: 'number', - updatedCells: 'number', - tableRange: 'string', + data: { type: 'json', description: 'Sheet data' }, + metadata: { type: 'json', description: 'Operation metadata' }, + updatedRange: { type: 'string', description: 'Updated range' }, + updatedRows: { type: 'number', description: 'Updated rows count' }, + updatedColumns: { type: 'number', description: 'Updated columns count' }, + updatedCells: { type: 'number', description: 'Updated cells count' }, + tableRange: { type: 'string', description: 'Table range' }, }, } diff --git a/apps/sim/blocks/blocks/huggingface.ts b/apps/sim/blocks/blocks/huggingface.ts index 22a88b338..8a7ea8722 100644 --- a/apps/sim/blocks/blocks/huggingface.ts +++ b/apps/sim/blocks/blocks/huggingface.ts @@ -26,6 +26,7 @@ export const HuggingFaceBlock: BlockConfig = { title: 'User Prompt', type: 'long-input', layout: 'full', + required: true, placeholder: 'Enter your message here...', rows: 3, }, @@ -34,6 +35,7 @@ export const HuggingFaceBlock: BlockConfig = { title: 'Provider', type: 'dropdown', layout: 'half', + required: true, options: [ { label: 'Novita', id: 'novita' }, { label: 'Cerebras', id: 'cerebras' }, @@ -55,6 +57,7 @@ export const HuggingFaceBlock: BlockConfig = { title: 'Model', type: 'short-input', layout: 'full', + required: true, placeholder: 'e.g., deepseek/deepseek-v3-0324, llama3.1-8b, meta-llama/Llama-3.2-3B-Instruct-Turbo', description: 'The model must be available for the selected provider.', @@ -80,6 +83,7 @@ export const HuggingFaceBlock: BlockConfig = { title: 'API Token', type: 'short-input', layout: 'full', + required: true, placeholder: 'Enter your Hugging Face API token', password: true, }, @@ -105,17 +109,17 @@ export const HuggingFaceBlock: BlockConfig = { }, }, inputs: { - systemPrompt: { type: 'string', required: false }, - content: { type: 'string', required: true }, - provider: { type: 'string', required: true }, - model: { type: 'string', required: true }, - temperature: { type: 'string', required: false }, - maxTokens: { type: 'string', required: false }, - apiKey: { type: 'string', required: true }, + systemPrompt: { type: 'string', description: 'System instructions' }, + content: { type: 'string', description: 'User message content' }, + provider: { type: 'string', description: 'Model provider' }, + model: { type: 'string', description: 'Model identifier' }, + temperature: { type: 'string', description: 'Response randomness' }, + maxTokens: { type: 'string', description: 'Maximum output tokens' }, + apiKey: { type: 'string', description: 'API access token' }, }, outputs: { - content: 'string', - model: 'string', - usage: 'json', + content: { type: 'string', description: 'Generated response' }, + model: { type: 'string', description: 'Model used' }, + usage: { type: 'json', description: 'Token usage stats' }, }, } diff --git a/apps/sim/blocks/blocks/hunter.ts b/apps/sim/blocks/blocks/hunter.ts new file mode 100644 index 000000000..91e2dec23 --- /dev/null +++ b/apps/sim/blocks/blocks/hunter.ts @@ -0,0 +1,269 @@ +import { HunterIOIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' +import type { HunterResponse } from '@/tools/hunter/types' + +export const HunterBlock: BlockConfig = { + type: 'hunter', + name: 'Hunter io', + description: 'Find and verify professional email addresses', + longDescription: + "Search for email addresses, verify their deliverability, discover companies, and enrich contact data using Hunter.io's powerful email finding capabilities.", + docsLink: 'https://docs.sim.ai/tools/hunter', + category: 'tools', + bgColor: '#E0E0E0', + icon: HunterIOIcon, + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'Domain Search', id: 'hunter_domain_search' }, + { label: 'Email Finder', id: 'hunter_email_finder' }, + { label: 'Email Verifier', id: 'hunter_email_verifier' }, + { label: 'Discover Companies', id: 'hunter_discover' }, + { label: 'Find Company', id: 'hunter_companies_find' }, + { label: 'Email Count', id: 'hunter_email_count' }, + ], + value: () => 'hunter_domain_search', + }, + // Domain Search operation inputs + { + id: 'domain', + title: 'Domain', + type: 'short-input', + layout: 'full', + required: true, + placeholder: 'Enter domain name (e.g., stripe.com)', + condition: { field: 'operation', value: 'hunter_domain_search' }, + }, + { + id: 'limit', + title: 'Number of Results', + type: 'short-input', + layout: 'full', + placeholder: '10', + condition: { field: 'operation', value: 'hunter_domain_search' }, + }, + { + id: 'type', + title: 'Email Type', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'All', id: 'all' }, + { label: 'Personal', id: 'personal' }, + { label: 'Generic', id: 'generic' }, + ], + value: () => 'all', + condition: { field: 'operation', value: 'hunter_domain_search' }, + }, + { + id: 'seniority', + title: 'Seniority Level', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'All', id: 'all' }, + { label: 'Junior', id: 'junior' }, + { label: 'Senior', id: 'senior' }, + { label: 'Executive', id: 'executive' }, + ], + value: () => 'all', + condition: { field: 'operation', value: 'hunter_domain_search' }, + }, + { + id: 'department', + title: 'Department', + type: 'short-input', + layout: 'full', + placeholder: 'e.g., sales, marketing, engineering', + condition: { field: 'operation', value: 'hunter_domain_search' }, + }, + // Email Finder operation inputs + { + id: 'domain', + title: 'Domain', + type: 'short-input', + layout: 'full', + required: true, + placeholder: 'Enter domain name (e.g., stripe.com)', + condition: { field: 'operation', value: 'hunter_email_finder' }, + }, + { + id: 'first_name', + title: 'First Name', + type: 'short-input', + layout: 'full', + required: true, + placeholder: 'Enter first name', + condition: { field: 'operation', value: 'hunter_email_finder' }, + }, + { + id: 'last_name', + title: 'Last Name', + type: 'short-input', + layout: 'full', + required: true, + placeholder: 'Enter last name', + condition: { field: 'operation', value: 'hunter_email_finder' }, + }, + { + id: 'company', + title: 'Company Name', + type: 'short-input', + layout: 'full', + placeholder: 'Enter company name', + condition: { field: 'operation', value: 'hunter_email_finder' }, + }, + // Email Verifier operation inputs + { + id: 'email', + title: 'Email Address', + type: 'short-input', + layout: 'full', + required: true, + placeholder: 'Enter email address to verify', + condition: { field: 'operation', value: 'hunter_email_verifier' }, + }, + // Discover operation inputs + { + id: 'query', + title: 'Search Query', + type: 'long-input', + layout: 'full', + placeholder: 'Enter search query (e.g., "software companies in San Francisco")', + condition: { field: 'operation', value: 'hunter_discover' }, + required: true, + }, + { + id: 'domain', + title: 'Domain Filter', + type: 'short-input', + layout: 'full', + placeholder: 'Filter by domain', + condition: { field: 'operation', value: 'hunter_discover' }, + }, + + // Find Company operation inputs + { + id: 'domain', + title: 'Domain', + type: 'short-input', + layout: 'full', + required: true, + placeholder: 'Enter company domain', + condition: { field: 'operation', value: 'hunter_companies_find' }, + }, + // Email Count operation inputs + { + id: 'domain', + title: 'Domain', + type: 'short-input', + layout: 'full', + placeholder: 'Enter domain name', + condition: { field: 'operation', value: 'hunter_email_count' }, + required: true, + }, + { + id: 'company', + title: 'Company Name', + type: 'short-input', + layout: 'full', + placeholder: 'Enter company name', + condition: { field: 'operation', value: 'hunter_email_count' }, + }, + { + id: 'type', + title: 'Email Type', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'All', id: 'all' }, + { label: 'Personal', id: 'personal' }, + { label: 'Generic', id: 'generic' }, + ], + value: () => 'all', + condition: { field: 'operation', value: 'hunter_email_count' }, + }, + // API Key (common) + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + required: true, + placeholder: 'Enter your Hunter.io API key', + password: true, + }, + ], + tools: { + access: [ + 'hunter_discover', + 'hunter_domain_search', + 'hunter_email_finder', + 'hunter_email_verifier', + 'hunter_companies_find', + 'hunter_email_count', + ], + config: { + tool: (params) => { + // Convert numeric parameters + if (params.limit) { + params.limit = Number(params.limit) + } + + switch (params.operation) { + case 'hunter_discover': + return 'hunter_discover' + case 'hunter_domain_search': + return 'hunter_domain_search' + case 'hunter_email_finder': + return 'hunter_email_finder' + case 'hunter_email_verifier': + return 'hunter_email_verifier' + case 'hunter_companies_find': + return 'hunter_companies_find' + case 'hunter_email_count': + return 'hunter_email_count' + default: + return 'hunter_domain_search' + } + }, + }, + }, + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + apiKey: { type: 'string', description: 'Hunter.io API key' }, + // Domain Search & Email Count + domain: { type: 'string', description: 'Company domain name' }, + limit: { type: 'number', description: 'Result limit' }, + offset: { type: 'number', description: 'Result offset' }, + type: { type: 'string', description: 'Email type filter' }, + seniority: { type: 'string', description: 'Seniority level filter' }, + department: { type: 'string', description: 'Department filter' }, + // Email Finder + first_name: { type: 'string', description: 'First name' }, + last_name: { type: 'string', description: 'Last name' }, + company: { type: 'string', description: 'Company name' }, + // Email Verifier & Enrichment + email: { type: 'string', description: 'Email address' }, + // Discover + query: { type: 'string', description: 'Search query' }, + headcount: { type: 'string', description: 'Company headcount filter' }, + company_type: { type: 'string', description: 'Company type filter' }, + technology: { type: 'string', description: 'Technology filter' }, + }, + outputs: { + results: { type: 'json', description: 'Search results' }, + emails: { type: 'json', description: 'Email addresses found' }, + email: { type: 'string', description: 'Found email address' }, + score: { type: 'number', description: 'Confidence score' }, + result: { type: 'string', description: 'Verification result' }, + status: { type: 'string', description: 'Status message' }, + total: { type: 'number', description: 'Total results count' }, + personal_emails: { type: 'number', description: 'Personal emails count' }, + generic_emails: { type: 'number', description: 'Generic emails count' }, + }, +} diff --git a/apps/sim/blocks/blocks/image_generator.ts b/apps/sim/blocks/blocks/image_generator.ts index 99053e1d6..e462cdc60 100644 --- a/apps/sim/blocks/blocks/image_generator.ts +++ b/apps/sim/blocks/blocks/image_generator.ts @@ -29,6 +29,7 @@ export const ImageGeneratorBlock: BlockConfig = { title: 'Prompt', type: 'long-input', layout: 'full', + required: true, placeholder: 'Describe the image you want to generate...', }, { @@ -100,6 +101,7 @@ export const ImageGeneratorBlock: BlockConfig = { title: 'API Key', type: 'short-input', layout: 'full', + required: true, placeholder: 'Enter your OpenAI API key', password: true, connectionDroppable: false, @@ -144,17 +146,17 @@ export const ImageGeneratorBlock: BlockConfig = { }, }, inputs: { - prompt: { type: 'string', required: true }, - model: { type: 'string', required: true }, - size: { type: 'string', required: false }, - quality: { type: 'string', required: false }, - style: { type: 'string', required: false }, - background: { type: 'string', required: false }, - apiKey: { type: 'string', required: true }, + prompt: { type: 'string', description: 'Image description prompt' }, + model: { type: 'string', description: 'Image generation model' }, + size: { type: 'string', description: 'Image dimensions' }, + quality: { type: 'string', description: 'Image quality level' }, + style: { type: 'string', description: 'Image style' }, + background: { type: 'string', description: 'Background type' }, + apiKey: { type: 'string', description: 'OpenAI API key' }, }, outputs: { - content: 'string', - image: 'string', - metadata: 'json', + content: { type: 'string', description: 'Generation response' }, + image: { type: 'string', description: 'Generated image URL' }, + metadata: { type: 'json', description: 'Generation metadata' }, }, } diff --git a/apps/sim/blocks/blocks/jina.ts b/apps/sim/blocks/blocks/jina.ts index e53c7e3af..0bf49b1aa 100644 --- a/apps/sim/blocks/blocks/jina.ts +++ b/apps/sim/blocks/blocks/jina.ts @@ -18,6 +18,7 @@ export const JinaBlock: BlockConfig = { title: 'URL', type: 'short-input', layout: 'full', + required: true, placeholder: 'Enter URL to extract content from', }, { @@ -26,9 +27,9 @@ export const JinaBlock: BlockConfig = { type: 'checkbox-list', layout: 'full', options: [ - { id: 'useReaderLMv2', label: 'Use Reader LM v2' }, - { id: 'gatherLinks', label: 'Gather Links' }, - { id: 'jsonResponse', label: 'JSON Response' }, + { label: 'Use Reader LM v2', id: 'useReaderLMv2' }, + { label: 'Gather Links', id: 'gatherLinks' }, + { label: 'JSON Response', id: 'jsonResponse' }, ], }, { @@ -36,6 +37,7 @@ export const JinaBlock: BlockConfig = { title: 'API Key', type: 'short-input', layout: 'full', + required: true, placeholder: 'Enter your Jina API key', password: true, }, @@ -44,13 +46,13 @@ export const JinaBlock: BlockConfig = { access: ['jina_read_url'], }, inputs: { - url: { type: 'string', required: true }, - useReaderLMv2: { type: 'boolean', required: false }, - gatherLinks: { type: 'boolean', required: false }, - jsonResponse: { type: 'boolean', required: false }, - apiKey: { type: 'string', required: true }, + url: { type: 'string', description: 'URL to extract' }, + useReaderLMv2: { type: 'boolean', description: 'Use Reader LM v2' }, + gatherLinks: { type: 'boolean', description: 'Gather page links' }, + jsonResponse: { type: 'boolean', description: 'JSON response format' }, + apiKey: { type: 'string', description: 'Jina API key' }, }, outputs: { - content: 'string', + content: { type: 'string', description: 'Extracted content' }, }, } diff --git a/apps/sim/blocks/blocks/jira.ts b/apps/sim/blocks/blocks/jira.ts index c985d4ec0..399ed00ee 100644 --- a/apps/sim/blocks/blocks/jira.ts +++ b/apps/sim/blocks/blocks/jira.ts @@ -24,12 +24,14 @@ export const JiraBlock: BlockConfig = { { label: 'Update Issue', id: 'update' }, { label: 'Write Issue', id: 'write' }, ], + value: () => 'read', }, { id: 'domain', title: 'Domain', type: 'short-input', layout: 'full', + required: true, placeholder: 'Enter Jira domain (e.g., simstudio.atlassian.net)', }, { @@ -37,6 +39,7 @@ export const JiraBlock: BlockConfig = { title: 'Jira Account', type: 'oauth-input', layout: 'full', + required: true, provider: 'jira', serviceId: 'jira', requiredScopes: [ @@ -97,6 +100,7 @@ export const JiraBlock: BlockConfig = { title: 'New Summary', type: 'short-input', layout: 'full', + required: true, placeholder: 'Enter new summary for the issue', condition: { field: 'operation', value: ['update', 'write'] }, }, @@ -222,27 +226,27 @@ export const JiraBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - domain: { type: 'string', required: true }, - credential: { type: 'string', required: true }, - issueKey: { type: 'string', required: false }, - projectId: { type: 'string', required: false }, - manualProjectId: { type: 'string', required: false }, - manualIssueKey: { type: 'string', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + domain: { type: 'string', description: 'Jira domain' }, + credential: { type: 'string', description: 'Jira access token' }, + issueKey: { type: 'string', description: 'Issue key identifier' }, + projectId: { type: 'string', description: 'Project identifier' }, + manualProjectId: { type: 'string', description: 'Manual project identifier' }, + manualIssueKey: { type: 'string', description: 'Manual issue key' }, // Update operation inputs - summary: { type: 'string', required: true }, - description: { type: 'string', required: false }, + summary: { type: 'string', description: 'Issue summary' }, + description: { type: 'string', description: 'Issue description' }, // Write operation inputs - issueType: { type: 'string', required: false }, + issueType: { type: 'string', description: 'Issue type' }, }, outputs: { - ts: 'string', - issueKey: 'string', - summary: 'string', - description: 'string', - created: 'string', - updated: 'string', - success: 'boolean', - url: 'string', + ts: { type: 'string', description: 'Timestamp' }, + issueKey: { type: 'string', description: 'Issue key' }, + summary: { type: 'string', description: 'Issue summary' }, + description: { type: 'string', description: 'Issue description' }, + created: { type: 'string', description: 'Creation date' }, + updated: { type: 'string', description: 'Update date' }, + success: { type: 'boolean', description: 'Operation success' }, + url: { type: 'string', description: 'Issue URL' }, }, } diff --git a/apps/sim/blocks/blocks/knowledge.ts b/apps/sim/blocks/blocks/knowledge.ts index 02610aa6c..c47bfd504 100644 --- a/apps/sim/blocks/blocks/knowledge.ts +++ b/apps/sim/blocks/blocks/knowledge.ts @@ -11,6 +11,102 @@ export const KnowledgeBlock: BlockConfig = { icon: PackageSearchIcon, category: 'blocks', docsLink: 'https://docs.sim.ai/blocks/knowledge', + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'Search', id: 'search' }, + { label: 'Upload Chunk', id: 'upload_chunk' }, + { label: 'Create Document', id: 'create_document' }, + ], + value: () => 'search', + }, + { + id: 'knowledgeBaseId', + title: 'Knowledge Base', + type: 'knowledge-base-selector', + layout: 'full', + placeholder: 'Select knowledge base', + multiSelect: false, + required: true, + condition: { field: 'operation', value: ['search', 'upload_chunk', 'create_document'] }, + }, + { + id: 'query', + title: 'Search Query', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your search query', + required: true, + condition: { field: 'operation', value: 'search' }, + }, + { + id: 'topK', + title: 'Number of Results', + type: 'short-input', + layout: 'full', + placeholder: 'Enter number of results (default: 10)', + condition: { field: 'operation', value: 'search' }, + }, + { + id: 'tagFilters', + title: 'Tag Filters', + type: 'knowledge-tag-filters', + layout: 'full', + placeholder: 'Add tag filters', + condition: { field: 'operation', value: 'search' }, + mode: 'advanced', + }, + { + id: 'documentId', + title: 'Document', + type: 'document-selector', + layout: 'full', + placeholder: 'Select document', + required: true, + condition: { field: 'operation', value: 'upload_chunk' }, + }, + { + id: 'content', + title: 'Chunk Content', + type: 'long-input', + layout: 'full', + placeholder: 'Enter the chunk content to upload', + rows: 6, + required: true, + condition: { field: 'operation', value: 'upload_chunk' }, + }, + { + id: 'name', + title: 'Document Name', + type: 'short-input', + layout: 'full', + placeholder: 'Enter document name', + required: true, + condition: { field: 'operation', value: ['create_document'] }, + }, + { + id: 'content', + title: 'Document Content', + type: 'long-input', + layout: 'full', + placeholder: 'Enter the document content', + rows: 6, + required: true, + condition: { field: 'operation', value: ['create_document'] }, + }, + // Dynamic tag entry for Create Document + { + id: 'documentTags', + title: 'Document Tags', + type: 'document-tag-entry', + layout: 'full', + condition: { field: 'operation', value: 'create_document' }, + }, + ], tools: { access: ['knowledge_search', 'knowledge_upload_chunk', 'knowledge_create_document'], config: { @@ -48,111 +144,21 @@ export const KnowledgeBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - knowledgeBaseId: { type: 'string', required: false }, - query: { type: 'string', required: false }, - topK: { type: 'number', required: false }, - documentId: { type: 'string', required: false }, - content: { type: 'string', required: false }, - name: { type: 'string', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + knowledgeBaseId: { type: 'string', description: 'Knowledge base identifier' }, + query: { type: 'string', description: 'Search query terms' }, + topK: { type: 'number', description: 'Number of results' }, + documentId: { type: 'string', description: 'Document identifier' }, + content: { type: 'string', description: 'Content data' }, + name: { type: 'string', description: 'Document name' }, // Dynamic tag filters for search - tagFilters: { type: 'string', required: false }, + tagFilters: { type: 'string', description: 'Tag filter criteria' }, // Document tags for create document (JSON string of tag objects) - documentTags: { type: 'string', required: false }, + documentTags: { type: 'string', description: 'Document tags' }, }, outputs: { - results: 'json', - query: 'string', - totalResults: 'number', + results: { type: 'json', description: 'Search results' }, + query: { type: 'string', description: 'Query used' }, + totalResults: { type: 'number', description: 'Total results count' }, }, - subBlocks: [ - { - id: 'operation', - title: 'Operation', - type: 'dropdown', - layout: 'full', - options: [ - { label: 'Search', id: 'search' }, - { label: 'Upload Chunk', id: 'upload_chunk' }, - { label: 'Create Document', id: 'create_document' }, - ], - value: () => 'search', - }, - { - id: 'knowledgeBaseId', - title: 'Knowledge Base', - type: 'knowledge-base-selector', - layout: 'full', - placeholder: 'Select knowledge base', - multiSelect: false, - condition: { field: 'operation', value: ['search', 'upload_chunk', 'create_document'] }, - }, - { - id: 'query', - title: 'Search Query', - type: 'short-input', - layout: 'full', - placeholder: 'Enter your search query', - condition: { field: 'operation', value: 'search' }, - }, - { - id: 'topK', - title: 'Number of Results', - type: 'short-input', - layout: 'full', - placeholder: 'Enter number of results (default: 10)', - condition: { field: 'operation', value: 'search' }, - }, - { - id: 'tagFilters', - title: 'Tag Filters', - type: 'knowledge-tag-filters', - layout: 'full', - placeholder: 'Add tag filters', - condition: { field: 'operation', value: 'search' }, - mode: 'advanced', - }, - { - id: 'documentId', - title: 'Document', - type: 'document-selector', - layout: 'full', - placeholder: 'Select document', - condition: { field: 'operation', value: 'upload_chunk' }, - }, - { - id: 'content', - title: 'Chunk Content', - type: 'long-input', - layout: 'full', - placeholder: 'Enter the chunk content to upload', - rows: 6, - condition: { field: 'operation', value: 'upload_chunk' }, - }, - { - id: 'name', - title: 'Document Name', - type: 'short-input', - layout: 'full', - placeholder: 'Enter document name', - condition: { field: 'operation', value: ['create_document'] }, - }, - { - id: 'content', - title: 'Document Content', - type: 'long-input', - layout: 'full', - placeholder: 'Enter the document content', - rows: 6, - condition: { field: 'operation', value: ['create_document'] }, - }, - // Dynamic tag entry for Create Document - { - id: 'documentTags', - title: 'Document Tags', - type: 'document-tag-entry', - layout: 'full', - condition: { field: 'operation', value: 'create_document' }, - }, - ], } diff --git a/apps/sim/blocks/blocks/linear.ts b/apps/sim/blocks/blocks/linear.ts index 8e3c35223..dee9f96e3 100644 --- a/apps/sim/blocks/blocks/linear.ts +++ b/apps/sim/blocks/blocks/linear.ts @@ -21,6 +21,7 @@ export const LinearBlock: BlockConfig = { { label: 'Read Issues', id: 'read' }, { label: 'Create Issue', id: 'write' }, ], + value: () => 'read', }, { id: 'credential', @@ -31,6 +32,7 @@ export const LinearBlock: BlockConfig = { serviceId: 'linear', requiredScopes: ['read', 'write'], placeholder: 'Select Linear account', + required: true, }, { id: 'teamId', @@ -76,6 +78,7 @@ export const LinearBlock: BlockConfig = { type: 'short-input', layout: 'full', condition: { field: 'operation', value: ['write'] }, + required: true, }, { id: 'description', @@ -130,17 +133,17 @@ export const LinearBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - credential: { 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 }, + operation: { type: 'string', description: 'Operation to perform' }, + credential: { type: 'string', description: 'Linear access token' }, + teamId: { type: 'string', description: 'Linear team identifier' }, + projectId: { type: 'string', description: 'Linear project identifier' }, + manualTeamId: { type: 'string', description: 'Manual team identifier' }, + manualProjectId: { type: 'string', description: 'Manual project identifier' }, + title: { type: 'string', description: 'Issue title' }, + description: { type: 'string', description: 'Issue description' }, }, outputs: { - issues: 'json', - issue: 'json', + issues: { type: 'json', description: 'Issues list' }, + issue: { type: 'json', description: 'Single issue data' }, }, } diff --git a/apps/sim/blocks/blocks/linkup.ts b/apps/sim/blocks/blocks/linkup.ts index 6c7340cbc..7c9926b18 100644 --- a/apps/sim/blocks/blocks/linkup.ts +++ b/apps/sim/blocks/blocks/linkup.ts @@ -20,6 +20,7 @@ export const LinkupBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'Enter your search query', + required: true, }, { id: 'outputType', @@ -30,6 +31,7 @@ export const LinkupBlock: BlockConfig = { { label: 'Answer', id: 'sourcedAnswer' }, { label: 'Search', id: 'searchResults' }, ], + value: () => 'sourcedAnswer', }, { id: 'depth', @@ -48,6 +50,7 @@ export const LinkupBlock: BlockConfig = { layout: 'full', placeholder: 'Enter your Linkup API key', password: true, + required: true, }, ], @@ -56,14 +59,14 @@ export const LinkupBlock: BlockConfig = { }, inputs: { - q: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, - depth: { type: 'string', required: true }, - outputType: { type: 'string', required: true }, + q: { type: 'string', description: 'Search query' }, + apiKey: { type: 'string', description: 'Linkup API key' }, + depth: { type: 'string', description: 'Search depth level' }, + outputType: { type: 'string', description: 'Output format type' }, }, outputs: { - answer: 'string', - sources: 'json', + answer: { type: 'string', description: 'Generated answer' }, + sources: { type: 'json', description: 'Source references' }, }, } diff --git a/apps/sim/blocks/blocks/mem0.ts b/apps/sim/blocks/blocks/mem0.ts index e6e3abc09..5822f593d 100644 --- a/apps/sim/blocks/blocks/mem0.ts +++ b/apps/sim/blocks/blocks/mem0.ts @@ -24,6 +24,7 @@ export const Mem0Block: BlockConfig = { { label: 'Get Memories', id: 'get' }, ], placeholder: 'Select an operation', + value: () => 'add', }, { id: 'userId', @@ -32,6 +33,7 @@ export const Mem0Block: BlockConfig = { layout: 'full', placeholder: 'Enter user identifier', value: () => 'userid', // Default to the working user ID from curl example + required: true, }, { id: 'messages', @@ -44,6 +46,7 @@ export const Mem0Block: BlockConfig = { field: 'operation', value: 'add', }, + required: true, }, { id: 'query', @@ -55,6 +58,7 @@ export const Mem0Block: BlockConfig = { field: 'operation', value: 'search', }, + required: true, }, { id: 'memoryId', @@ -96,6 +100,7 @@ export const Mem0Block: BlockConfig = { layout: 'full', placeholder: 'Enter your Mem0 API key', password: true, + required: true, }, { id: 'limit', @@ -278,20 +283,20 @@ export const Mem0Block: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, - userId: { type: 'string', required: true }, - version: { type: 'string', required: false }, - messages: { type: 'json', required: false }, - query: { type: 'string', required: false }, - memoryId: { type: 'string', required: false }, - startDate: { type: 'string', required: false }, - endDate: { type: 'string', required: false }, - limit: { type: 'number', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + apiKey: { type: 'string', description: 'Mem0 API key' }, + userId: { type: 'string', description: 'User identifier' }, + version: { type: 'string', description: 'API version' }, + messages: { type: 'json', description: 'Message data array' }, + query: { type: 'string', description: 'Search query' }, + memoryId: { type: 'string', description: 'Memory identifier' }, + startDate: { type: 'string', description: 'Start date filter' }, + endDate: { type: 'string', description: 'End date filter' }, + limit: { type: 'number', description: 'Result limit' }, }, outputs: { - ids: 'any', - memories: 'any', - searchResults: 'any', + ids: { type: 'any', description: 'Memory identifiers' }, + memories: { type: 'any', description: 'Memory data' }, + searchResults: { type: 'any', description: 'Search results' }, }, } diff --git a/apps/sim/blocks/blocks/memory.ts b/apps/sim/blocks/blocks/memory.ts index 6fb8ef970..f4c478169 100644 --- a/apps/sim/blocks/blocks/memory.ts +++ b/apps/sim/blocks/blocks/memory.ts @@ -11,6 +11,87 @@ export const MemoryBlock: BlockConfig = { icon: BrainIcon, category: 'blocks', docsLink: 'https://docs.sim.ai/tools/memory', + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'Add Memory', id: 'add' }, + { label: 'Get All Memories', id: 'getAll' }, + { label: 'Get Memory', id: 'get' }, + { label: 'Delete Memory', id: 'delete' }, + ], + placeholder: 'Select operation', + value: () => 'add', + }, + { + id: 'id', + title: 'ID', + type: 'short-input', + layout: 'full', + placeholder: 'Enter memory identifier', + condition: { + field: 'operation', + value: 'add', + }, + required: true, + }, + { + id: 'id', + title: 'ID', + type: 'short-input', + layout: 'full', + placeholder: 'Enter memory identifier to retrieve', + condition: { + field: 'operation', + value: 'get', + }, + required: true, + }, + { + id: 'id', + title: 'ID', + type: 'short-input', + layout: 'full', + placeholder: 'Enter memory identifier to delete', + condition: { + field: 'operation', + value: 'delete', + }, + required: true, + }, + { + id: 'role', + title: 'Role', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'User', id: 'user' }, + { label: 'Assistant', id: 'assistant' }, + { label: 'System', id: 'system' }, + ], + placeholder: 'Select agent role', + condition: { + field: 'operation', + value: 'add', + }, + required: true, + }, + { + id: 'content', + title: 'Content', + type: 'short-input', + layout: 'full', + placeholder: 'Enter message content', + condition: { + field: 'operation', + value: 'add', + }, + required: true, + }, + ], tools: { access: ['memory_add', 'memory_get', 'memory_get_all', 'memory_delete'], config: { @@ -99,88 +180,13 @@ export const MemoryBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - id: { type: 'string', required: true }, - role: { type: 'string', required: false }, - content: { type: 'string', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + id: { type: 'string', description: 'Memory identifier' }, + role: { type: 'string', description: 'Agent role' }, + content: { type: 'string', description: 'Memory content' }, }, outputs: { - memories: 'any', - id: 'string', + memories: { type: 'any', description: 'Memory data' }, + id: { type: 'string', description: 'Memory identifier' }, }, - subBlocks: [ - { - id: 'operation', - title: 'Operation', - type: 'dropdown', - layout: 'full', - options: [ - { label: 'Add Memory', id: 'add' }, - { label: 'Get All Memories', id: 'getAll' }, - { label: 'Get Memory', id: 'get' }, - { label: 'Delete Memory', id: 'delete' }, - ], - placeholder: 'Select operation', - }, - { - id: 'id', - title: 'ID', - type: 'short-input', - layout: 'full', - placeholder: 'Enter memory identifier', - condition: { - field: 'operation', - value: 'add', - }, - }, - { - id: 'id', - title: 'ID', - type: 'short-input', - layout: 'full', - placeholder: 'Enter memory identifier to retrieve', - condition: { - field: 'operation', - value: 'get', - }, - }, - { - id: 'id', - title: 'ID', - type: 'short-input', - layout: 'full', - placeholder: 'Enter memory identifier to delete', - condition: { - field: 'operation', - value: 'delete', - }, - }, - { - id: 'role', - title: 'Role', - type: 'dropdown', - layout: 'full', - options: [ - { label: 'User', id: 'user' }, - { label: 'Assistant', id: 'assistant' }, - { label: 'System', id: 'system' }, - ], - placeholder: 'Select agent role', - condition: { - field: 'operation', - value: 'add', - }, - }, - { - id: 'content', - title: 'Content', - type: 'short-input', - layout: 'full', - placeholder: 'Enter message content', - condition: { - field: 'operation', - value: 'add', - }, - }, - ], } diff --git a/apps/sim/blocks/blocks/microsoft_excel.ts b/apps/sim/blocks/blocks/microsoft_excel.ts index 04369ccbe..56c6fd553 100644 --- a/apps/sim/blocks/blocks/microsoft_excel.ts +++ b/apps/sim/blocks/blocks/microsoft_excel.ts @@ -23,6 +23,7 @@ export const MicrosoftExcelBlock: BlockConfig = { { label: 'Write/Update Data', id: 'write' }, { label: 'Add to Table', id: 'table_add' }, ], + value: () => 'read', }, { id: 'credential', @@ -33,6 +34,7 @@ export const MicrosoftExcelBlock: BlockConfig = { serviceId: 'microsoft-excel', requiredScopes: [], placeholder: 'Select Microsoft account', + required: true, }, { id: 'spreadsheetId', @@ -69,6 +71,7 @@ export const MicrosoftExcelBlock: BlockConfig = { layout: 'full', placeholder: 'Name of the Excel table', condition: { field: 'operation', value: ['table_add'] }, + required: true, }, { id: 'values', @@ -78,6 +81,7 @@ export const MicrosoftExcelBlock: BlockConfig = { placeholder: 'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])', condition: { field: 'operation', value: 'write' }, + required: true, }, { id: 'valueInputOption', @@ -98,6 +102,7 @@ export const MicrosoftExcelBlock: BlockConfig = { placeholder: 'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])', condition: { field: 'operation', value: 'update' }, + required: true, }, { id: 'valueInputOption', @@ -118,6 +123,7 @@ export const MicrosoftExcelBlock: BlockConfig = { placeholder: 'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])', condition: { field: 'operation', value: 'table_add' }, + required: true, }, ], tools: { @@ -181,23 +187,23 @@ export const MicrosoftExcelBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - credential: { type: 'string', required: true }, - spreadsheetId: { type: 'string', required: false }, - manualSpreadsheetId: { type: 'string', required: false }, - range: { type: 'string', required: false }, - tableName: { type: 'string', required: false }, - values: { type: 'string', required: false }, - valueInputOption: { type: 'string', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + credential: { type: 'string', description: 'Microsoft Excel access token' }, + spreadsheetId: { type: 'string', description: 'Spreadsheet identifier' }, + manualSpreadsheetId: { type: 'string', description: 'Manual spreadsheet identifier' }, + range: { type: 'string', description: 'Cell range' }, + tableName: { type: 'string', description: 'Table name' }, + values: { type: 'string', description: 'Cell values data' }, + valueInputOption: { type: 'string', description: 'Value input option' }, }, outputs: { - data: 'json', - metadata: 'json', - updatedRange: 'string', - updatedRows: 'number', - updatedColumns: 'number', - updatedCells: 'number', - index: 'number', - values: 'json', + data: { type: 'json', description: 'Sheet data' }, + metadata: { type: 'json', description: 'Operation metadata' }, + updatedRange: { type: 'string', description: 'Updated range' }, + updatedRows: { type: 'number', description: 'Updated rows count' }, + updatedColumns: { type: 'number', description: 'Updated columns count' }, + updatedCells: { type: 'number', description: 'Updated cells count' }, + index: { type: 'number', description: 'Row index' }, + values: { type: 'json', description: 'Table values' }, }, } diff --git a/apps/sim/blocks/blocks/microsoft_teams.ts b/apps/sim/blocks/blocks/microsoft_teams.ts index 60f37a506..d3cfb1861 100644 --- a/apps/sim/blocks/blocks/microsoft_teams.ts +++ b/apps/sim/blocks/blocks/microsoft_teams.ts @@ -24,6 +24,7 @@ export const MicrosoftTeamsBlock: BlockConfig = { { label: 'Read Channel Messages', id: 'read_channel' }, { label: 'Write Channel Message', id: 'write_channel' }, ], + value: () => 'read_chat', }, { id: 'credential', @@ -49,6 +50,7 @@ export const MicrosoftTeamsBlock: BlockConfig = { 'offline_access', ], placeholder: 'Select Microsoft account', + required: true, }, { id: 'teamId', @@ -121,6 +123,7 @@ export const MicrosoftTeamsBlock: BlockConfig = { layout: 'full', placeholder: 'Enter message content', condition: { field: 'operation', value: ['write_chat', 'write_channel'] }, + required: true, }, ], tools: { @@ -206,20 +209,20 @@ export const MicrosoftTeamsBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - credential: { 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 }, + operation: { type: 'string', description: 'Operation to perform' }, + credential: { type: 'string', description: 'Microsoft Teams access token' }, + messageId: { type: 'string', description: 'Message identifier' }, + chatId: { type: 'string', description: 'Chat identifier' }, + manualChatId: { type: 'string', description: 'Manual chat identifier' }, + channelId: { type: 'string', description: 'Channel identifier' }, + manualChannelId: { type: 'string', description: 'Manual channel identifier' }, + teamId: { type: 'string', description: 'Team identifier' }, + manualTeamId: { type: 'string', description: 'Manual team identifier' }, + content: { type: 'string', description: 'Message content' }, }, outputs: { - content: 'string', - metadata: 'json', - updatedContent: 'boolean', + content: { type: 'string', description: 'Message content' }, + metadata: { type: 'json', description: 'Message metadata' }, + updatedContent: { type: 'boolean', description: 'Content update status' }, }, } diff --git a/apps/sim/blocks/blocks/mistral_parse.ts b/apps/sim/blocks/blocks/mistral_parse.ts index 5d77f7696..596578d31 100644 --- a/apps/sim/blocks/blocks/mistral_parse.ts +++ b/apps/sim/blocks/blocks/mistral_parse.ts @@ -53,6 +53,7 @@ export const MistralParseBlock: BlockConfig = { type: 'short-input' as SubBlockType, layout: 'full' as SubBlockLayout, placeholder: 'Enter full URL to a PDF document (https://example.com/document.pdf)', + required: !shouldEnableFileUpload, ...(shouldEnableFileUpload ? { condition: { @@ -116,6 +117,7 @@ export const MistralParseBlock: BlockConfig = { layout: 'full' as SubBlockLayout, placeholder: 'Enter your Mistral API key', password: true, + required: true, }, ], tools: { @@ -191,19 +193,15 @@ export const MistralParseBlock: BlockConfig = { }, }, inputs: { - inputMethod: { type: 'string', required: false }, - filePath: { type: 'string', required: !shouldEnableFileUpload }, - fileUpload: { type: 'json', required: false }, - apiKey: { type: 'string', required: true }, - resultType: { type: 'string', required: false }, - pages: { type: 'string', required: false }, - // Image-related inputs - temporarily disabled - // includeImageBase64: { type: 'boolean', required: false }, - // imageLimit: { type: 'string', required: false }, - // imageMinSize: { type: 'string', required: false }, + inputMethod: { type: 'string', description: 'Input method selection' }, + filePath: { type: 'string', description: 'PDF document URL' }, + fileUpload: { type: 'json', description: 'Uploaded PDF file' }, + apiKey: { type: 'string', description: 'Mistral API key' }, + resultType: { type: 'string', description: 'Output format type' }, + pages: { type: 'string', description: 'Page selection' }, }, outputs: { - content: 'string', - metadata: 'json', + content: { type: 'string', description: 'Extracted content' }, + metadata: { type: 'json', description: 'Processing metadata' }, }, } diff --git a/apps/sim/blocks/blocks/notion.ts b/apps/sim/blocks/blocks/notion.ts index 49062e113..ab102cad9 100644 --- a/apps/sim/blocks/blocks/notion.ts +++ b/apps/sim/blocks/blocks/notion.ts @@ -19,18 +19,15 @@ export const NotionBlock: BlockConfig = { type: 'dropdown', layout: 'full', options: [ - // Read Operations { label: 'Read Page', id: 'notion_read' }, { label: 'Read Database', id: 'notion_read_database' }, - // Create Operations { label: 'Create Page', id: 'notion_create_page' }, { label: 'Create Database', id: 'notion_create_database' }, - // Write Operations { label: 'Append Content', id: 'notion_write' }, - // Query & Search Operations { label: 'Query Database', id: 'notion_query_database' }, { label: 'Search Workspace', id: 'notion_search' }, ], + value: () => 'notion_read', }, { id: 'credential', @@ -41,6 +38,7 @@ export const NotionBlock: BlockConfig = { serviceId: 'notion', requiredScopes: ['workspace.content', 'workspace.name', 'page.read', 'page.write'], placeholder: 'Select Notion account', + required: true, }, // Read/Write operation - Page ID { @@ -53,6 +51,7 @@ export const NotionBlock: BlockConfig = { field: 'operation', value: 'notion_read', }, + required: true, }, { id: 'databaseId', @@ -64,6 +63,7 @@ export const NotionBlock: BlockConfig = { field: 'operation', value: 'notion_read_database', }, + required: true, }, { id: 'pageId', @@ -75,6 +75,7 @@ export const NotionBlock: BlockConfig = { field: 'operation', value: 'notion_write', }, + required: true, }, // Create operation fields { @@ -84,6 +85,7 @@ export const NotionBlock: BlockConfig = { layout: 'full', placeholder: 'ID of parent page', condition: { field: 'operation', value: 'notion_create_page' }, + required: true, }, { id: 'title', @@ -107,6 +109,7 @@ export const NotionBlock: BlockConfig = { field: 'operation', value: 'notion_write', }, + required: true, }, { id: 'content', @@ -118,6 +121,7 @@ export const NotionBlock: BlockConfig = { field: 'operation', value: 'notion_create_page', }, + required: true, }, // Query Database Fields { @@ -127,6 +131,7 @@ export const NotionBlock: BlockConfig = { layout: 'full', placeholder: 'Enter Notion database ID', condition: { field: 'operation', value: 'notion_query_database' }, + required: true, }, { id: 'filter', @@ -135,6 +140,7 @@ export const NotionBlock: BlockConfig = { layout: 'full', placeholder: 'Enter filter conditions as JSON (optional)', condition: { field: 'operation', value: 'notion_query_database' }, + required: true, }, { id: 'sorts', @@ -181,6 +187,7 @@ export const NotionBlock: BlockConfig = { layout: 'full', placeholder: 'ID of parent page where database will be created', condition: { field: 'operation', value: 'notion_create_database' }, + required: true, }, { id: 'title', @@ -189,6 +196,7 @@ export const NotionBlock: BlockConfig = { layout: 'full', placeholder: 'Title for the new database', condition: { field: 'operation', value: 'notion_create_database' }, + required: true, }, { id: 'properties', @@ -283,24 +291,24 @@ export const NotionBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - credential: { type: 'string', required: true }, - pageId: { type: 'string', required: false }, - content: { type: 'string', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + credential: { type: 'string', description: 'Notion access token' }, + pageId: { type: 'string', description: 'Page identifier' }, + content: { type: 'string', description: 'Page content' }, // Create page inputs - parentId: { type: 'string', required: false }, - title: { type: 'string', required: false }, + parentId: { type: 'string', description: 'Parent page identifier' }, + title: { type: 'string', description: 'Page title' }, // Query database inputs - databaseId: { type: 'string', required: false }, - filter: { type: 'string', required: false }, - sorts: { type: 'string', required: false }, - pageSize: { type: 'number', required: false }, + databaseId: { type: 'string', description: 'Database identifier' }, + filter: { type: 'string', description: 'Filter criteria' }, + sorts: { type: 'string', description: 'Sort criteria' }, + pageSize: { type: 'number', description: 'Page size limit' }, // Search inputs - query: { type: 'string', required: false }, - filterType: { type: 'string', required: false }, + query: { type: 'string', description: 'Search query' }, + filterType: { type: 'string', description: 'Filter type' }, }, outputs: { - content: 'string', - metadata: 'any', + content: { type: 'string', description: 'Page content' }, + metadata: { type: 'any', description: 'Page metadata' }, }, } diff --git a/apps/sim/blocks/blocks/openai.ts b/apps/sim/blocks/blocks/openai.ts index a51deab3e..992af8c80 100644 --- a/apps/sim/blocks/blocks/openai.ts +++ b/apps/sim/blocks/blocks/openai.ts @@ -18,6 +18,7 @@ export const OpenAIBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'Enter text to generate embeddings for', + required: true, }, { id: 'model', @@ -38,19 +39,20 @@ export const OpenAIBlock: BlockConfig = { layout: 'full', placeholder: 'Enter your OpenAI API key', password: true, + required: true, }, ], tools: { access: ['openai_embeddings'], }, inputs: { - input: { type: 'string', required: true }, - model: { type: 'string', required: false }, - apiKey: { type: 'string', required: true }, + input: { type: 'string', description: 'Text to embed' }, + model: { type: 'string', description: 'Embedding model' }, + apiKey: { type: 'string', description: 'OpenAI API key' }, }, outputs: { - embeddings: 'json', - model: 'string', - usage: 'json', + embeddings: { type: 'json', description: 'Generated embeddings' }, + model: { type: 'string', description: 'Model used' }, + usage: { type: 'json', description: 'Token usage' }, }, } diff --git a/apps/sim/blocks/blocks/outlook.ts b/apps/sim/blocks/blocks/outlook.ts index e737b6824..8728e5efd 100644 --- a/apps/sim/blocks/blocks/outlook.ts +++ b/apps/sim/blocks/blocks/outlook.ts @@ -23,6 +23,7 @@ export const OutlookBlock: BlockConfig = { { label: 'Draft Email', id: 'draft_outlook' }, { label: 'Read Email', id: 'read_outlook' }, ], + value: () => 'send_outlook', }, { id: 'credential', @@ -42,6 +43,7 @@ export const OutlookBlock: BlockConfig = { 'email', ], placeholder: 'Select Microsoft account', + required: true, }, { id: 'to', @@ -50,6 +52,7 @@ export const OutlookBlock: BlockConfig = { layout: 'full', placeholder: 'Recipient email address', condition: { field: 'operation', value: ['send_outlook', 'draft_outlook'] }, + required: true, }, { id: 'subject', @@ -58,6 +61,7 @@ export const OutlookBlock: BlockConfig = { layout: 'full', placeholder: 'Email subject', condition: { field: 'operation', value: ['send_outlook', 'draft_outlook'] }, + required: true, }, { id: 'body', @@ -66,6 +70,7 @@ export const OutlookBlock: BlockConfig = { layout: 'full', placeholder: 'Email content', condition: { field: 'operation', value: ['send_outlook', 'draft_outlook'] }, + required: true, }, // Read Email Fields - Add folder selector (basic mode) { @@ -134,19 +139,19 @@ export const OutlookBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - credential: { type: 'string', required: true }, + operation: { type: 'string', description: 'Operation to perform' }, + credential: { type: 'string', description: 'Outlook access token' }, // Send operation inputs - to: { type: 'string', required: false }, - subject: { type: 'string', required: false }, - body: { type: 'string', required: false }, + to: { type: 'string', description: 'Recipient email address' }, + subject: { type: 'string', description: 'Email subject' }, + body: { type: 'string', description: 'Email content' }, // Read operation inputs - folder: { type: 'string', required: false }, - manualFolder: { type: 'string', required: false }, - maxResults: { type: 'number', required: false }, + folder: { type: 'string', description: 'Email folder' }, + manualFolder: { type: 'string', description: 'Manual folder name' }, + maxResults: { type: 'number', description: 'Maximum emails' }, }, outputs: { - message: 'string', - results: 'json', + message: { type: 'string', description: 'Response message' }, + results: { type: 'json', description: 'Email results' }, }, } diff --git a/apps/sim/blocks/blocks/perplexity.ts b/apps/sim/blocks/blocks/perplexity.ts index 083c9eeae..f1c894c01 100644 --- a/apps/sim/blocks/blocks/perplexity.ts +++ b/apps/sim/blocks/blocks/perplexity.ts @@ -18,7 +18,7 @@ export const PerplexityBlock: BlockConfig = { title: 'System Prompt', type: 'long-input', layout: 'full', - placeholder: 'Optional system prompt to guide the model behavior...', + placeholder: 'System prompt to guide the model behavior...', }, { id: 'content', @@ -26,6 +26,7 @@ export const PerplexityBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'Enter your prompt here...', + required: true, }, { id: 'model', @@ -65,6 +66,7 @@ export const PerplexityBlock: BlockConfig = { layout: 'full', placeholder: 'Enter your Perplexity API key', password: true, + required: true, }, ], tools: { @@ -86,16 +88,16 @@ export const PerplexityBlock: BlockConfig = { }, }, inputs: { - content: { type: 'string', required: true }, - systemPrompt: { type: 'string', required: false }, - model: { type: 'string', required: true }, - max_tokens: { type: 'string', required: false }, - temperature: { type: 'string', required: false }, - apiKey: { type: 'string', required: true }, + content: { type: 'string', description: 'User prompt content' }, + systemPrompt: { type: 'string', description: 'System instructions' }, + model: { type: 'string', description: 'AI model to use' }, + max_tokens: { type: 'string', description: 'Maximum output tokens' }, + temperature: { type: 'string', description: 'Response randomness' }, + apiKey: { type: 'string', description: 'Perplexity API key' }, }, outputs: { - content: 'string', - model: 'string', - usage: 'json', + content: { type: 'string', description: 'Generated response' }, + model: { type: 'string', description: 'Model used' }, + usage: { type: 'json', description: 'Token usage' }, }, } diff --git a/apps/sim/blocks/blocks/pinecone.ts b/apps/sim/blocks/blocks/pinecone.ts index 020cd1c5b..6d85eefb3 100644 --- a/apps/sim/blocks/blocks/pinecone.ts +++ b/apps/sim/blocks/blocks/pinecone.ts @@ -26,6 +26,7 @@ export const PineconeBlock: BlockConfig = { { label: 'Search With Vector', id: 'search_vector' }, { label: 'Fetch Vectors', id: 'fetch' }, ], + value: () => 'generate', }, // Generate embeddings fields { @@ -42,6 +43,7 @@ export const PineconeBlock: BlockConfig = { }, ], condition: { field: 'operation', value: 'generate' }, + value: () => 'multilingual-e5-large', }, { id: 'inputs', @@ -50,6 +52,7 @@ export const PineconeBlock: BlockConfig = { layout: 'full', placeholder: '[{"text": "Your text here"}]', condition: { field: 'operation', value: 'generate' }, + required: true, }, // Upsert text fields { @@ -59,6 +62,7 @@ export const PineconeBlock: BlockConfig = { layout: 'full', placeholder: 'https://index-name-abc123.svc.project-id.pinecone.io', condition: { field: 'operation', value: 'upsert_text' }, + required: true, }, { id: 'namespace', @@ -67,6 +71,7 @@ export const PineconeBlock: BlockConfig = { layout: 'full', placeholder: 'default', condition: { field: 'operation', value: 'upsert_text' }, + required: true, }, { id: 'records', @@ -76,6 +81,7 @@ export const PineconeBlock: BlockConfig = { placeholder: '{"_id": "rec1", "text": "Apple\'s first product, the Apple I, was released in 1976.", "category": "product"}\n{"_id": "rec2", "chunk_text": "Apples are a great source of dietary fiber.", "category": "nutrition"}', condition: { field: 'operation', value: 'upsert_text' }, + required: true, }, // Search text fields { @@ -85,6 +91,7 @@ export const PineconeBlock: BlockConfig = { layout: 'full', placeholder: 'https://index-name-abc123.svc.project-id.pinecone.io', condition: { field: 'operation', value: 'search_text' }, + required: true, }, { id: 'namespace', @@ -93,6 +100,7 @@ export const PineconeBlock: BlockConfig = { layout: 'full', placeholder: 'default', condition: { field: 'operation', value: 'search_text' }, + required: true, }, { id: 'searchQuery', @@ -101,6 +109,7 @@ export const PineconeBlock: BlockConfig = { layout: 'full', placeholder: 'Enter text to search for', condition: { field: 'operation', value: 'search_text' }, + required: true, }, { id: 'topK', @@ -142,6 +151,7 @@ export const PineconeBlock: BlockConfig = { layout: 'full', placeholder: 'https://index-name-abc123.svc.project-id.pinecone.io', condition: { field: 'operation', value: 'fetch' }, + required: true, }, { id: 'namespace', @@ -150,6 +160,7 @@ export const PineconeBlock: BlockConfig = { layout: 'full', placeholder: 'Namespace', condition: { field: 'operation', value: 'fetch' }, + required: true, }, { id: 'ids', @@ -158,6 +169,7 @@ export const PineconeBlock: BlockConfig = { layout: 'full', placeholder: '["vec1", "vec2"]', condition: { field: 'operation', value: 'fetch' }, + required: true, }, // Add vector search fields { @@ -167,6 +179,7 @@ export const PineconeBlock: BlockConfig = { layout: 'full', placeholder: 'https://index-name-abc123.svc.project-id.pinecone.io', condition: { field: 'operation', value: 'search_vector' }, + required: true, }, { id: 'namespace', @@ -175,6 +188,7 @@ export const PineconeBlock: BlockConfig = { layout: 'full', placeholder: 'default', condition: { field: 'operation', value: 'search_vector' }, + required: true, }, { id: 'vector', @@ -183,6 +197,7 @@ export const PineconeBlock: BlockConfig = { layout: 'full', placeholder: '[0.1, 0.2, 0.3, ...]', condition: { field: 'operation', value: 'search_vector' }, + required: true, }, { id: 'topK', @@ -211,6 +226,7 @@ export const PineconeBlock: BlockConfig = { layout: 'full', placeholder: 'Your Pinecone API key', password: true, + required: true, }, ], @@ -243,35 +259,35 @@ export const PineconeBlock: BlockConfig = { }, inputs: { - operation: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, - indexHost: { type: 'string', required: false }, - namespace: { type: 'string', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + apiKey: { type: 'string', description: 'Pinecone API key' }, + indexHost: { type: 'string', description: 'Index host URL' }, + namespace: { type: 'string', description: 'Vector namespace' }, // Generate embeddings inputs - model: { type: 'string', required: false }, - inputs: { type: 'json', required: false }, - parameters: { type: 'json', required: false }, + model: { type: 'string', description: 'Embedding model' }, + inputs: { type: 'json', description: 'Text inputs' }, + parameters: { type: 'json', description: 'Model parameters' }, // Upsert text inputs - records: { type: 'json', required: false }, + records: { type: 'json', description: 'Records to upsert' }, // Search text inputs - searchQuery: { type: 'string', required: false }, - topK: { type: 'string', required: false }, - fields: { type: 'json', required: false }, - filter: { type: 'json', required: false }, - rerank: { type: 'json', required: false }, + searchQuery: { type: 'string', description: 'Search query text' }, + topK: { type: 'string', description: 'Top K results' }, + fields: { type: 'json', description: 'Fields to return' }, + filter: { type: 'json', description: 'Search filter' }, + rerank: { type: 'json', description: 'Rerank options' }, // Fetch inputs - ids: { type: 'json', required: false }, - vector: { type: 'json', required: false }, - includeValues: { type: 'boolean', required: false }, - includeMetadata: { type: 'boolean', required: false }, + ids: { type: 'json', description: 'Vector identifiers' }, + vector: { type: 'json', description: 'Query vector' }, + includeValues: { type: 'boolean', description: 'Include vector values' }, + includeMetadata: { type: 'boolean', description: 'Include metadata' }, }, outputs: { - matches: 'any', - upsertedCount: 'any', - data: 'any', - model: 'any', - vector_type: 'any', - usage: 'any', + matches: { type: 'any', description: 'Search matches' }, + upsertedCount: { type: 'any', description: 'Upserted count' }, + data: { type: 'any', description: 'Response data' }, + model: { type: 'any', description: 'Model information' }, + vector_type: { type: 'any', description: 'Vector type' }, + usage: { type: 'any', description: 'Usage statistics' }, }, } diff --git a/apps/sim/blocks/blocks/qdrant.ts b/apps/sim/blocks/blocks/qdrant.ts index 1074fabcd..1dbc6e11b 100644 --- a/apps/sim/blocks/blocks/qdrant.ts +++ b/apps/sim/blocks/blocks/qdrant.ts @@ -24,6 +24,7 @@ export const QdrantBlock: BlockConfig = { { label: 'Search', id: 'search' }, { label: 'Fetch', id: 'fetch' }, ], + value: () => 'upsert', }, // Upsert fields { @@ -33,15 +34,7 @@ export const QdrantBlock: BlockConfig = { layout: 'full', placeholder: 'http://localhost:6333', condition: { field: 'operation', value: 'upsert' }, - }, - { - id: 'apiKey', - title: 'API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Your Qdrant API key (optional)', - password: true, - condition: { field: 'operation', value: 'upsert' }, + required: true, }, { id: 'collection', @@ -50,6 +43,7 @@ export const QdrantBlock: BlockConfig = { layout: 'full', placeholder: 'my-collection', condition: { field: 'operation', value: 'upsert' }, + required: true, }, { id: 'points', @@ -58,6 +52,7 @@ export const QdrantBlock: BlockConfig = { layout: 'full', placeholder: '[{"id": 1, "vector": [0.1, 0.2], "payload": {"category": "a"}}]', condition: { field: 'operation', value: 'upsert' }, + required: true, }, // Search fields { @@ -67,15 +62,7 @@ export const QdrantBlock: BlockConfig = { layout: 'full', placeholder: 'http://localhost:6333', condition: { field: 'operation', value: 'search' }, - }, - { - id: 'apiKey', - title: 'API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Your Qdrant API key (optional)', - password: true, - condition: { field: 'operation', value: 'search' }, + required: true, }, { id: 'collection', @@ -84,6 +71,7 @@ export const QdrantBlock: BlockConfig = { layout: 'full', placeholder: 'my-collection', condition: { field: 'operation', value: 'search' }, + required: true, }, { id: 'vector', @@ -92,6 +80,7 @@ export const QdrantBlock: BlockConfig = { layout: 'full', placeholder: '[0.1, 0.2]', condition: { field: 'operation', value: 'search' }, + required: true, }, { id: 'limit', @@ -131,15 +120,7 @@ export const QdrantBlock: BlockConfig = { layout: 'full', placeholder: 'http://localhost:6333', condition: { field: 'operation', value: 'fetch' }, - }, - { - id: 'apiKey', - title: 'API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Your Qdrant API key (optional)', - password: true, - condition: { field: 'operation', value: 'fetch' }, + required: true, }, { id: 'collection', @@ -148,6 +129,7 @@ export const QdrantBlock: BlockConfig = { layout: 'full', placeholder: 'my-collection', condition: { field: 'operation', value: 'fetch' }, + required: true, }, { id: 'ids', @@ -156,6 +138,7 @@ export const QdrantBlock: BlockConfig = { layout: 'full', placeholder: '["370446a3-310f-58db-8ce7-31db947c6c1e"]', condition: { field: 'operation', value: 'fetch' }, + required: true, }, { id: 'with_payload', @@ -171,6 +154,15 @@ export const QdrantBlock: BlockConfig = { layout: 'full', condition: { field: 'operation', value: 'fetch' }, }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Your Qdrant API key (optional)', + password: true, + required: true, + }, ], tools: { @@ -192,23 +184,23 @@ export const QdrantBlock: BlockConfig = { }, inputs: { - operation: { type: 'string', required: true }, - url: { type: 'string', required: true }, - apiKey: { type: 'string', required: false }, - collection: { type: 'string', required: true }, - points: { type: 'json', required: false }, - vector: { type: 'json', required: false }, - limit: { type: 'number', required: false }, - filter: { type: 'json', required: false }, - ids: { type: 'json', required: false }, - with_payload: { type: 'boolean', required: false }, - with_vector: { type: 'boolean', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + url: { type: 'string', description: 'Qdrant server URL' }, + apiKey: { type: 'string', description: 'Qdrant API key' }, + collection: { type: 'string', description: 'Collection name' }, + points: { type: 'json', description: 'Points to upsert' }, + vector: { type: 'json', description: 'Query vector' }, + limit: { type: 'number', description: 'Result limit' }, + filter: { type: 'json', description: 'Search filter' }, + ids: { type: 'json', description: 'Point identifiers' }, + with_payload: { type: 'boolean', description: 'Include payload' }, + with_vector: { type: 'boolean', description: 'Include vectors' }, }, outputs: { - matches: 'any', - upsertedCount: 'any', - data: 'any', - status: 'any', + matches: { type: 'any', description: 'Search matches' }, + upsertedCount: { type: 'any', description: 'Upserted count' }, + data: { type: 'any', description: 'Response data' }, + status: { type: 'any', description: 'Operation status' }, }, } diff --git a/apps/sim/blocks/blocks/reddit.ts b/apps/sim/blocks/blocks/reddit.ts index b3feff335..154a0b564 100644 --- a/apps/sim/blocks/blocks/reddit.ts +++ b/apps/sim/blocks/blocks/reddit.ts @@ -23,6 +23,7 @@ export const RedditBlock: BlockConfig = { { label: 'Get Posts', id: 'get_posts' }, { label: 'Get Comments', id: 'get_comments' }, ], + value: () => 'get_posts', }, // Reddit OAuth Authentication @@ -35,6 +36,7 @@ export const RedditBlock: BlockConfig = { serviceId: 'reddit', requiredScopes: ['identity', 'read'], placeholder: 'Select Reddit account', + required: true, }, // Common fields - appear for all actions @@ -48,6 +50,7 @@ export const RedditBlock: BlockConfig = { field: 'operation', value: ['get_posts', 'get_comments'], }, + required: true, }, // Get Posts specific fields @@ -66,6 +69,7 @@ export const RedditBlock: BlockConfig = { field: 'operation', value: 'get_posts', }, + required: true, }, { id: 'time', @@ -111,6 +115,7 @@ export const RedditBlock: BlockConfig = { field: 'operation', value: 'get_comments', }, + required: true, }, { id: 'commentSort', @@ -180,20 +185,20 @@ export const RedditBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - credential: { type: 'string', required: true }, - subreddit: { type: 'string', required: true }, - sort: { type: 'string', required: true }, - time: { type: 'string', required: false }, - limit: { type: 'number', required: false }, - postId: { type: 'string', required: true }, - commentSort: { type: 'string', required: false }, - commentLimit: { type: 'number', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + credential: { type: 'string', description: 'Reddit access token' }, + subreddit: { type: 'string', description: 'Subreddit name' }, + sort: { type: 'string', description: 'Sort order' }, + time: { type: 'string', description: 'Time filter' }, + limit: { type: 'number', description: 'Maximum posts' }, + postId: { type: 'string', description: 'Post identifier' }, + commentSort: { type: 'string', description: 'Comment sort order' }, + commentLimit: { type: 'number', description: 'Maximum comments' }, }, outputs: { - subreddit: 'string', - posts: 'json', - post: 'json', - comments: 'json', + subreddit: { type: 'string', description: 'Subreddit name' }, + posts: { type: 'json', description: 'Posts data' }, + post: { type: 'json', description: 'Single post data' }, + comments: { type: 'json', description: 'Comments data' }, }, } diff --git a/apps/sim/blocks/blocks/response.ts b/apps/sim/blocks/blocks/response.ts index 3938b9739..e860746d9 100644 --- a/apps/sim/blocks/blocks/response.ts +++ b/apps/sim/blocks/blocks/response.ts @@ -90,33 +90,28 @@ Example: inputs: { dataMode: { type: 'string', - required: false, - description: 'Mode for defining response data structure', + description: 'Response data definition mode', }, builderData: { type: 'json', - required: false, - description: 'The JSON data to send in the response body', + description: 'Structured response data', }, data: { type: 'json', - required: false, - description: 'The JSON data to send in the response body', + description: 'JSON response body', }, status: { type: 'number', - required: false, - description: 'HTTP status code (default: 200)', + description: 'HTTP status code', }, headers: { type: 'json', - required: false, - description: 'Additional response headers', + description: 'Response headers', }, }, outputs: { - data: 'json', - status: 'number', - headers: 'json', + data: { type: 'json', description: 'Response data' }, + status: { type: 'number', description: 'HTTP status code' }, + headers: { type: 'json', description: 'Response headers' }, }, } diff --git a/apps/sim/blocks/blocks/router.ts b/apps/sim/blocks/blocks/router.ts index 059ca0682..97fa9d9d5 100644 --- a/apps/sim/blocks/blocks/router.ts +++ b/apps/sim/blocks/blocks/router.ts @@ -111,6 +111,7 @@ export const RouterBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'Route to the correct block based on the input...', + required: true, }, { id: 'model', @@ -125,6 +126,7 @@ export const RouterBlock: BlockConfig = { id: model, })) }, + required: true, }, { id: 'apiKey', @@ -134,6 +136,7 @@ export const RouterBlock: BlockConfig = { placeholder: 'Enter your API key', password: true, connectionDroppable: false, + required: true, // Hide API key for all hosted models when running on hosted version condition: isHosted ? { @@ -178,15 +181,15 @@ export const RouterBlock: BlockConfig = { }, }, inputs: { - prompt: { type: 'string', required: true }, - model: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, + prompt: { type: 'string', description: 'Routing prompt content' }, + model: { type: 'string', description: 'AI model to use' }, + apiKey: { type: 'string', description: 'Provider API key' }, }, outputs: { - content: 'string', - model: 'string', - tokens: 'any', - cost: 'any', - selectedPath: 'json', + content: { type: 'string', description: 'Routing response content' }, + model: { type: 'string', description: 'Model used' }, + tokens: { type: 'any', description: 'Token usage' }, + cost: { type: 'any', description: 'Cost information' }, + selectedPath: { type: 'json', description: 'Selected routing path' }, }, } diff --git a/apps/sim/blocks/blocks/s3.ts b/apps/sim/blocks/blocks/s3.ts index fb937c0e7..539d1ba16 100644 --- a/apps/sim/blocks/blocks/s3.ts +++ b/apps/sim/blocks/blocks/s3.ts @@ -19,6 +19,7 @@ export const S3Block: BlockConfig = { layout: 'full', placeholder: 'Enter your AWS Access Key ID', password: true, + required: true, }, { id: 'secretAccessKey', @@ -27,6 +28,7 @@ export const S3Block: BlockConfig = { layout: 'full', placeholder: 'Enter your AWS Secret Access Key', password: true, + required: true, }, { id: 's3Uri', @@ -34,6 +36,7 @@ export const S3Block: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'e.g., https://bucket-name.s3.region.amazonaws.com/path/to/file', + required: true, }, ], tools: { @@ -91,12 +94,12 @@ export const S3Block: BlockConfig = { }, }, inputs: { - accessKeyId: { type: 'string', required: true }, - secretAccessKey: { type: 'string', required: true }, - s3Uri: { type: 'string', required: true }, + accessKeyId: { type: 'string', description: 'AWS access key ID' }, + secretAccessKey: { type: 'string', description: 'AWS secret access key' }, + s3Uri: { type: 'string', description: 'S3 object URL' }, }, outputs: { - url: 'string', - metadata: 'json', + url: { type: 'string', description: 'Presigned URL' }, + metadata: { type: 'json', description: 'Object metadata' }, }, } diff --git a/apps/sim/blocks/blocks/serper.ts b/apps/sim/blocks/blocks/serper.ts index 068dc9eeb..1b5bf0531 100644 --- a/apps/sim/blocks/blocks/serper.ts +++ b/apps/sim/blocks/blocks/serper.ts @@ -19,6 +19,7 @@ export const SerperBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter your search query...', + required: true, }, { id: 'type', @@ -31,6 +32,7 @@ export const SerperBlock: BlockConfig = { { label: 'places', id: 'places' }, { label: 'images', id: 'images' }, ], + value: () => 'search', }, { id: 'num', @@ -80,20 +82,21 @@ export const SerperBlock: BlockConfig = { layout: 'full', placeholder: 'Enter your Serper API key', password: true, + required: true, }, ], tools: { access: ['serper_search'], }, inputs: { - query: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, - num: { type: 'number', required: false }, - gl: { type: 'string', required: false }, - hl: { type: 'string', required: false }, - type: { type: 'string', required: false }, + query: { type: 'string', description: 'Search query terms' }, + apiKey: { type: 'string', description: 'Serper API key' }, + num: { type: 'number', description: 'Number of results' }, + gl: { type: 'string', description: 'Country code' }, + hl: { type: 'string', description: 'Language code' }, + type: { type: 'string', description: 'Search type' }, }, outputs: { - searchResults: 'json', + searchResults: { type: 'json', description: 'Search results data' }, }, } diff --git a/apps/sim/blocks/blocks/slack.ts b/apps/sim/blocks/blocks/slack.ts index 7bfe220c1..a11623139 100644 --- a/apps/sim/blocks/blocks/slack.ts +++ b/apps/sim/blocks/blocks/slack.ts @@ -35,6 +35,7 @@ export const SlackBlock: BlockConfig = { { label: 'Custom Bot', id: 'bot_token' }, ], value: () => 'oauth', + required: true, }, { id: 'credential', @@ -100,6 +101,7 @@ export const SlackBlock: BlockConfig = { field: 'operation', value: 'send', }, + required: true, }, // Canvas specific fields { @@ -112,6 +114,7 @@ export const SlackBlock: BlockConfig = { field: 'operation', value: 'canvas', }, + required: true, }, { id: 'content', @@ -123,6 +126,7 @@ export const SlackBlock: BlockConfig = { field: 'operation', value: 'canvas', }, + required: true, }, // Message Reader specific fields { @@ -130,7 +134,7 @@ export const SlackBlock: BlockConfig = { title: 'Message Limit', type: 'short-input', layout: 'half', - placeholder: '50', + placeholder: '15', condition: { field: 'operation', value: 'read', @@ -240,23 +244,23 @@ export const SlackBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - authMethod: { type: 'string', required: true }, - credential: { type: 'string', required: false }, - botToken: { type: 'string', required: false }, - channel: { type: 'string', required: false }, - manualChannel: { type: 'string', required: false }, - text: { type: 'string', required: false }, - title: { type: 'string', required: false }, - content: { type: 'string', required: false }, - limit: { type: 'string', required: false }, - oldest: { type: 'string', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + authMethod: { type: 'string', description: 'Authentication method' }, + credential: { type: 'string', description: 'Slack access token' }, + botToken: { type: 'string', description: 'Bot token' }, + channel: { type: 'string', description: 'Channel identifier' }, + manualChannel: { type: 'string', description: 'Manual channel identifier' }, + text: { type: 'string', description: 'Message text' }, + title: { type: 'string', description: 'Canvas title' }, + content: { type: 'string', description: 'Canvas content' }, + limit: { type: 'string', description: 'Message limit' }, + oldest: { type: 'string', description: 'Oldest timestamp' }, }, outputs: { - ts: 'string', - channel: 'string', - canvas_id: 'string', - title: 'string', - messages: 'json', + ts: { type: 'string', description: 'Message timestamp' }, + channel: { type: 'string', description: 'Channel identifier' }, + canvas_id: { type: 'string', description: 'Canvas identifier' }, + title: { type: 'string', description: 'Canvas title' }, + messages: { type: 'json', description: 'Message data' }, }, } diff --git a/apps/sim/blocks/blocks/stagehand.ts b/apps/sim/blocks/blocks/stagehand.ts index 84bffd9cc..0721d4a6a 100644 --- a/apps/sim/blocks/blocks/stagehand.ts +++ b/apps/sim/blocks/blocks/stagehand.ts @@ -25,13 +25,15 @@ export const StagehandBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter the URL of the website to extract data from', + required: true, }, { id: 'instruction', - title: 'Instruction', + title: 'Instructions', type: 'long-input', layout: 'full', placeholder: 'Enter detailed instructions for what data to extract from the page...', + required: true, }, { id: 'apiKey', @@ -40,6 +42,7 @@ export const StagehandBlock: BlockConfig = { layout: 'full', placeholder: 'Enter your OpenAI API key', password: true, + required: true, }, { id: 'schema', @@ -49,6 +52,7 @@ export const StagehandBlock: BlockConfig = { placeholder: 'Enter JSON Schema...', language: 'json', generationType: 'json-schema', + required: true, }, ], tools: { @@ -58,12 +62,12 @@ export const StagehandBlock: BlockConfig = { }, }, inputs: { - url: { type: 'string', required: true }, - instruction: { type: 'string', required: true }, - schema: { type: 'json', required: true }, - apiKey: { type: 'string', required: true }, + url: { type: 'string', description: 'Website URL to extract' }, + instruction: { type: 'string', description: 'Extraction instructions' }, + schema: { type: 'json', description: 'JSON schema definition' }, + apiKey: { type: 'string', description: 'OpenAI API key' }, }, outputs: { - data: 'json', + data: { type: 'json', description: 'Extracted data' }, }, } diff --git a/apps/sim/blocks/blocks/stagehand_agent.ts b/apps/sim/blocks/blocks/stagehand_agent.ts index a6d0edc00..f638b659f 100644 --- a/apps/sim/blocks/blocks/stagehand_agent.ts +++ b/apps/sim/blocks/blocks/stagehand_agent.ts @@ -19,6 +19,7 @@ export const StagehandAgentBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter the starting URL for the agent', + required: true, }, { id: 'task', @@ -27,6 +28,7 @@ export const StagehandAgentBlock: BlockConfig = { layout: 'full', placeholder: 'Enter the task or goal for the agent to achieve. Reference variables using %key% syntax.', + required: true, }, { id: 'variables', @@ -42,6 +44,7 @@ export const StagehandAgentBlock: BlockConfig = { layout: 'full', placeholder: 'Enter your Anthropic API key', password: true, + required: true, }, { id: 'outputSchema', @@ -60,14 +63,14 @@ export const StagehandAgentBlock: BlockConfig = { }, }, inputs: { - startUrl: { type: 'string', required: true }, - task: { type: 'string', required: true }, - variables: { type: 'json', required: false }, - apiKey: { type: 'string', required: true }, - outputSchema: { type: 'json', required: false }, + startUrl: { type: 'string', description: 'Starting URL for agent' }, + task: { type: 'string', description: 'Task description' }, + variables: { type: 'json', description: 'Task variables' }, + apiKey: { type: 'string', description: 'Anthropic API key' }, + outputSchema: { type: 'json', description: 'Output schema' }, }, outputs: { - agentResult: 'json', - structuredOutput: 'any', + agentResult: { type: 'json', description: 'Agent execution result' }, + structuredOutput: { type: 'any', description: 'Structured output data' }, }, } diff --git a/apps/sim/blocks/blocks/starter.ts b/apps/sim/blocks/blocks/starter.ts index 412b67069..88ac56a2c 100644 --- a/apps/sim/blocks/blocks/starter.ts +++ b/apps/sim/blocks/blocks/starter.ts @@ -36,7 +36,7 @@ export const StarterBlock: BlockConfig = { access: [], }, inputs: { - input: { type: 'json', required: false }, + input: { type: 'json', description: 'Workflow input data' }, }, - outputs: {}, + outputs: {}, // No outputs - starter blocks initiate workflow execution } diff --git a/apps/sim/blocks/blocks/supabase.ts b/apps/sim/blocks/blocks/supabase.ts index 8498b612f..eb22dc678 100644 --- a/apps/sim/blocks/blocks/supabase.ts +++ b/apps/sim/blocks/blocks/supabase.ts @@ -28,6 +28,7 @@ export const SupabaseBlock: BlockConfig = { { label: 'Update a Row', id: 'update' }, { label: 'Delete a Row', id: 'delete' }, ], + value: () => 'query', }, { id: 'projectId', @@ -36,6 +37,7 @@ export const SupabaseBlock: BlockConfig = { layout: 'full', password: true, placeholder: 'Your Supabase project ID (e.g., jdrkgepadsdopsntdlom)', + required: true, }, { id: 'table', @@ -43,6 +45,7 @@ export const SupabaseBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Name of the table', + required: true, }, { id: 'apiKey', @@ -51,6 +54,7 @@ export const SupabaseBlock: BlockConfig = { layout: 'full', placeholder: 'Your Supabase service role secret key', password: true, + required: true, }, // Data input for create/update operations { @@ -60,6 +64,7 @@ export const SupabaseBlock: BlockConfig = { layout: 'full', placeholder: '{\n "column1": "value1",\n "column2": "value2"\n}', condition: { field: 'operation', value: 'insert' }, + required: true, }, { id: 'data', @@ -68,6 +73,7 @@ export const SupabaseBlock: BlockConfig = { layout: 'full', placeholder: '{\n "column1": "value1",\n "column2": "value2"\n}', condition: { field: 'operation', value: 'update' }, + required: true, }, // Filter for get_row, update, delete operations (required) { @@ -77,6 +83,7 @@ export const SupabaseBlock: BlockConfig = { layout: 'full', placeholder: 'id=eq.123', condition: { field: 'operation', value: 'get_row' }, + required: true, }, { id: 'filter', @@ -85,6 +92,7 @@ export const SupabaseBlock: BlockConfig = { layout: 'full', placeholder: 'id=eq.123', condition: { field: 'operation', value: 'update' }, + required: true, }, { id: 'filter', @@ -93,6 +101,7 @@ export const SupabaseBlock: BlockConfig = { layout: 'full', placeholder: 'id=eq.123', condition: { field: 'operation', value: 'delete' }, + required: true, }, // Optional filter for query operation { @@ -184,20 +193,20 @@ export const SupabaseBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - projectId: { type: 'string', required: true }, - table: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, + operation: { type: 'string', description: 'Operation to perform' }, + projectId: { type: 'string', description: 'Supabase project identifier' }, + table: { type: 'string', description: 'Database table name' }, + apiKey: { type: 'string', description: 'Service role secret key' }, // Data for insert/update operations - data: { type: 'json', required: false }, + data: { type: 'json', description: 'Row data' }, // Filter for operations - filter: { type: 'string', required: false }, + filter: { type: 'string', description: 'PostgREST filter syntax' }, // Query operation inputs - orderBy: { type: 'string', required: false }, - limit: { type: 'number', required: false }, + orderBy: { type: 'string', description: 'Sort column' }, + limit: { type: 'number', description: 'Result limit' }, }, outputs: { - message: 'string', - results: 'json', + message: { type: 'string', description: 'Operation message' }, + results: { type: 'json', description: 'Query results' }, }, } diff --git a/apps/sim/blocks/blocks/tavily.ts b/apps/sim/blocks/blocks/tavily.ts index 97ced6d22..f07d550ca 100644 --- a/apps/sim/blocks/blocks/tavily.ts +++ b/apps/sim/blocks/blocks/tavily.ts @@ -24,14 +24,6 @@ export const TavilyBlock: BlockConfig = { ], value: () => 'tavily_search', }, - { - id: 'apiKey', - title: 'API Key', - type: 'short-input', - layout: 'full', - placeholder: 'Enter your Tavily API key', - password: true, - }, { id: 'query', title: 'Search Query', @@ -39,6 +31,7 @@ export const TavilyBlock: BlockConfig = { layout: 'full', placeholder: 'Enter your search query...', condition: { field: 'operation', value: 'tavily_search' }, + required: true, }, { id: 'maxResults', @@ -55,6 +48,7 @@ export const TavilyBlock: BlockConfig = { layout: 'full', placeholder: 'Enter URL to extract content from...', condition: { field: 'operation', value: 'tavily_extract' }, + required: true, }, { id: 'extract_depth', @@ -68,6 +62,15 @@ export const TavilyBlock: BlockConfig = { value: () => 'basic', condition: { field: 'operation', value: 'tavily_extract' }, }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your Tavily API key', + password: true, + required: true, + }, ], tools: { access: ['tavily_search', 'tavily_extract'], @@ -85,19 +88,19 @@ export const TavilyBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, - query: { type: 'string', required: false }, - maxResults: { type: 'number', required: false }, - urls: { type: 'string', required: false }, - extract_depth: { type: 'string', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + apiKey: { type: 'string', description: 'Tavily API key' }, + query: { type: 'string', description: 'Search query terms' }, + maxResults: { type: 'number', description: 'Maximum search results' }, + urls: { type: 'string', description: 'URL to extract' }, + extract_depth: { type: 'string', description: 'Extraction depth level' }, }, outputs: { - results: 'json', - answer: 'any', - query: 'string', - content: 'string', - title: 'string', - url: 'string', + results: { type: 'json', description: 'Search results data' }, + answer: { type: 'any', description: 'Search answer' }, + query: { type: 'string', description: 'Query used' }, + content: { type: 'string', description: 'Extracted content' }, + title: { type: 'string', description: 'Page title' }, + url: { type: 'string', description: 'Source URL' }, }, } diff --git a/apps/sim/blocks/blocks/telegram.ts b/apps/sim/blocks/blocks/telegram.ts index a35d89883..3c67e6f62 100644 --- a/apps/sim/blocks/blocks/telegram.ts +++ b/apps/sim/blocks/blocks/telegram.ts @@ -25,6 +25,7 @@ export const TelegramBlock: BlockConfig = { 1. If you haven't already, message "/newbot" to @BotFather 2. Choose a name for your bot 3. Copy the token it provides and paste it here`, + required: true, }, { id: 'chatId', @@ -37,6 +38,7 @@ export const TelegramBlock: BlockConfig = { 2. Send any message to the channel (e.g. "I love Sim") 3. Visit https://api.telegram.org/bot/getUpdates 4. Look for the chat field in the JSON response at the very bottomwhere you'll find the chat ID`, + required: true, }, { id: 'text', @@ -44,18 +46,19 @@ export const TelegramBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'Enter the message to send', + required: true, }, ], tools: { access: ['telegram_message'], }, inputs: { - botToken: { type: 'string', required: true }, - chatId: { type: 'string', required: true }, - text: { type: 'string', required: true }, + botToken: { type: 'string', description: 'Telegram bot token' }, + chatId: { type: 'string', description: 'Chat identifier' }, + text: { type: 'string', description: 'Message text' }, }, outputs: { - ok: 'boolean', - result: 'json', + ok: { type: 'boolean', description: 'Success status' }, + result: { type: 'json', description: 'Message result' }, }, } diff --git a/apps/sim/blocks/blocks/thinking.ts b/apps/sim/blocks/blocks/thinking.ts index a22a778a6..8f7c60098 100644 --- a/apps/sim/blocks/blocks/thinking.ts +++ b/apps/sim/blocks/blocks/thinking.ts @@ -22,15 +22,16 @@ export const ThinkingBlock: BlockConfig = { layout: 'full', placeholder: 'Describe the step-by-step thinking process here...', hidden: true, + required: true, }, ], inputs: { - thought: { type: 'string', required: true }, + thought: { type: 'string', description: 'Thinking process instructions' }, }, outputs: { - acknowledgedThought: 'string', + acknowledgedThought: { type: 'string', description: 'Acknowledged thought process' }, }, tools: { diff --git a/apps/sim/blocks/blocks/translate.ts b/apps/sim/blocks/blocks/translate.ts index 07f1e5a2b..a0f446046 100644 --- a/apps/sim/blocks/blocks/translate.ts +++ b/apps/sim/blocks/blocks/translate.ts @@ -31,6 +31,7 @@ export const TranslateBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'Enter the text you want to translate', + required: true, }, { id: 'targetLanguage', @@ -38,6 +39,7 @@ export const TranslateBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter language (e.g. Spanish, French, etc.)', + required: true, }, { id: 'model', @@ -45,6 +47,7 @@ export const TranslateBlock: BlockConfig = { type: 'dropdown', layout: 'half', options: Object.keys(getBaseModelProviders()).map((key) => ({ label: key, id: key })), + required: true, }, { id: 'apiKey', @@ -54,6 +57,7 @@ export const TranslateBlock: BlockConfig = { placeholder: 'Enter your API key', password: true, connectionDroppable: false, + required: true, }, { id: 'systemPrompt', @@ -87,14 +91,14 @@ export const TranslateBlock: BlockConfig = { }, }, inputs: { - context: { type: 'string', required: true }, - targetLanguage: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, - systemPrompt: { type: 'string', required: true }, + context: { type: 'string', description: 'Text to translate' }, + targetLanguage: { type: 'string', description: 'Target language' }, + apiKey: { type: 'string', description: 'Provider API key' }, + systemPrompt: { type: 'string', description: 'Translation instructions' }, }, outputs: { - content: 'string', - model: 'string', - tokens: 'any', + content: { type: 'string', description: 'Translated text' }, + model: { type: 'string', description: 'Model used' }, + tokens: { type: 'any', description: 'Token usage' }, }, } diff --git a/apps/sim/blocks/blocks/twilio.ts b/apps/sim/blocks/blocks/twilio.ts index c20bcfcca..c47c25928 100644 --- a/apps/sim/blocks/blocks/twilio.ts +++ b/apps/sim/blocks/blocks/twilio.ts @@ -17,6 +17,7 @@ export const TwilioSMSBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'Enter phone numbers with country code (one per line, e.g., +1234567890)', + required: true, }, { id: 'message', @@ -24,6 +25,7 @@ export const TwilioSMSBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'e.g. "Hello! This is a test message."', + required: true, }, { id: 'accountSid', @@ -31,6 +33,7 @@ export const TwilioSMSBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Your Twilio Account SID', + required: true, }, { id: 'authToken', @@ -39,6 +42,7 @@ export const TwilioSMSBlock: BlockConfig = { layout: 'full', placeholder: 'Your Twilio Auth Token', password: true, + required: true, }, { id: 'fromNumber', @@ -46,6 +50,7 @@ export const TwilioSMSBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'e.g. +1234567890', + required: true, }, ], tools: { @@ -55,16 +60,16 @@ export const TwilioSMSBlock: BlockConfig = { }, }, inputs: { - phoneNumbers: { type: 'string', required: true }, - message: { type: 'string', required: true }, - accountSid: { type: 'string', required: true }, - authToken: { type: 'string', required: true }, - fromNumber: { type: 'string', required: true }, + phoneNumbers: { type: 'string', description: 'Recipient phone numbers' }, + message: { type: 'string', description: 'SMS message text' }, + accountSid: { type: 'string', description: 'Twilio account SID' }, + authToken: { type: 'string', description: 'Twilio auth token' }, + fromNumber: { type: 'string', description: 'Sender phone number' }, }, outputs: { - success: 'boolean', - messageId: 'any', - status: 'any', - error: 'any', + success: { type: 'boolean', description: 'Send success status' }, + messageId: { type: 'any', description: 'Message identifier' }, + status: { type: 'any', description: 'Delivery status' }, + error: { type: 'any', description: 'Error information' }, }, } diff --git a/apps/sim/blocks/blocks/typeform.ts b/apps/sim/blocks/blocks/typeform.ts index 140ea523c..8a39daabc 100644 --- a/apps/sim/blocks/blocks/typeform.ts +++ b/apps/sim/blocks/blocks/typeform.ts @@ -31,6 +31,7 @@ export const TypeformBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter your Typeform form ID', + required: true, }, { id: 'apiKey', @@ -39,6 +40,7 @@ export const TypeformBlock: BlockConfig = { layout: 'full', placeholder: 'Enter your Typeform personal access token', password: true, + required: true, }, // Response operation fields { @@ -128,23 +130,23 @@ export const TypeformBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - formId: { type: 'string', required: true }, - apiKey: { type: 'string', required: true }, + operation: { type: 'string', description: 'Operation to perform' }, + formId: { type: 'string', description: 'Typeform form identifier' }, + apiKey: { type: 'string', description: 'Personal access token' }, // Response operation params - pageSize: { type: 'number', required: false }, - since: { type: 'string', required: false }, - until: { type: 'string', required: false }, - completed: { type: 'string', required: false }, + pageSize: { type: 'number', description: 'Responses per page' }, + since: { type: 'string', description: 'Start date filter' }, + until: { type: 'string', description: 'End date filter' }, + completed: { type: 'string', description: 'Completion status filter' }, // File operation params - responseId: { type: 'string', required: false }, - fieldId: { type: 'string', required: false }, - filename: { type: 'string', required: false }, - inline: { type: 'boolean', required: false }, + responseId: { type: 'string', description: 'Response identifier' }, + fieldId: { type: 'string', description: 'Field identifier' }, + filename: { type: 'string', description: 'File name' }, + inline: { type: 'boolean', description: 'Inline display option' }, }, outputs: { - total_items: 'number', - page_count: 'number', - items: 'json', + total_items: { type: 'number', description: 'Total response count' }, + page_count: { type: 'number', description: 'Total page count' }, + items: { type: 'json', description: 'Response items' }, }, } diff --git a/apps/sim/blocks/blocks/vision.ts b/apps/sim/blocks/blocks/vision.ts index be859901e..2dc9c29b5 100644 --- a/apps/sim/blocks/blocks/vision.ts +++ b/apps/sim/blocks/blocks/vision.ts @@ -19,6 +19,7 @@ export const VisionBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter publicly accessible image URL', + required: true, }, { id: 'model', @@ -30,6 +31,7 @@ export const VisionBlock: BlockConfig = { { label: 'claude-3-opus', id: 'claude-3-opus-20240229' }, { label: 'claude-3-sonnet', id: 'claude-3-sonnet-20240229' }, ], + value: () => 'gpt-4o', }, { id: 'prompt', @@ -37,6 +39,7 @@ export const VisionBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'Enter prompt for image analysis', + required: true, }, { id: 'apiKey', @@ -45,20 +48,21 @@ export const VisionBlock: BlockConfig = { layout: 'full', placeholder: 'Enter your API key', password: true, + required: true, }, ], tools: { access: ['vision_tool'], }, inputs: { - apiKey: { type: 'string', required: true }, - imageUrl: { type: 'string', required: true }, - model: { type: 'string', required: false }, - prompt: { type: 'string', required: false }, + apiKey: { type: 'string', description: 'Provider API key' }, + imageUrl: { type: 'string', description: 'Image URL' }, + model: { type: 'string', description: 'Vision model' }, + prompt: { type: 'string', description: 'Analysis prompt' }, }, outputs: { - content: 'string', - model: 'any', - tokens: 'any', + content: { type: 'string', description: 'Analysis result' }, + model: { type: 'any', description: 'Model used' }, + tokens: { type: 'any', description: 'Token usage' }, }, } diff --git a/apps/sim/blocks/blocks/wealthbox.ts b/apps/sim/blocks/blocks/wealthbox.ts index b60dcddb9..f50a3b248 100644 --- a/apps/sim/blocks/blocks/wealthbox.ts +++ b/apps/sim/blocks/blocks/wealthbox.ts @@ -26,6 +26,7 @@ export const WealthboxBlock: BlockConfig = { { label: 'Read Task', id: 'read_task' }, { label: 'Write Task', id: 'write_task' }, ], + value: () => 'read_note', }, { id: 'credential', @@ -36,6 +37,7 @@ export const WealthboxBlock: BlockConfig = { serviceId: 'wealthbox', requiredScopes: ['login', 'data'], placeholder: 'Select Wealthbox account', + required: true, }, { id: 'noteId', @@ -68,11 +70,8 @@ export const WealthboxBlock: BlockConfig = { }, { id: 'taskId', - title: 'Select Task', - type: 'file-selector', - provider: 'wealthbox', - serviceId: 'wealthbox', - requiredScopes: ['login', 'data'], + title: 'Task ID', + type: 'short-input', layout: 'full', placeholder: 'Enter Task ID', mode: 'basic', @@ -94,6 +93,7 @@ export const WealthboxBlock: BlockConfig = { layout: 'full', placeholder: 'Enter Title', condition: { field: 'operation', value: ['write_task'] }, + required: true, }, { id: 'dueDate', @@ -102,6 +102,7 @@ export const WealthboxBlock: BlockConfig = { layout: 'full', placeholder: 'Enter due date (e.g., 2015-05-24 11:00 AM -0400)', condition: { field: 'operation', value: ['write_task'] }, + required: true, }, { id: 'firstName', @@ -110,6 +111,7 @@ export const WealthboxBlock: BlockConfig = { layout: 'full', placeholder: 'Enter First Name', condition: { field: 'operation', value: ['write_contact'] }, + required: true, }, { id: 'lastName', @@ -118,6 +120,7 @@ export const WealthboxBlock: BlockConfig = { layout: 'full', placeholder: 'Enter Last Name', condition: { field: 'operation', value: ['write_contact'] }, + required: true, }, { id: 'emailAddress', @@ -134,6 +137,7 @@ export const WealthboxBlock: BlockConfig = { layout: 'full', placeholder: 'Enter Content', condition: { field: 'operation', value: ['write_note', 'write_event', 'write_task'] }, + required: true, }, { id: 'backgroundInformation', @@ -235,29 +239,29 @@ export const WealthboxBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - 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 }, - emailAddress: { type: 'string', required: false }, - backgroundInformation: { type: 'string', required: false }, - title: { type: 'string', required: false }, - dueDate: { type: 'string', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + credential: { type: 'string', description: 'Wealthbox access token' }, + noteId: { type: 'string', description: 'Note identifier' }, + contactId: { type: 'string', description: 'Contact identifier' }, + manualContactId: { type: 'string', description: 'Manual contact identifier' }, + taskId: { type: 'string', description: 'Task identifier' }, + manualTaskId: { type: 'string', description: 'Manual task identifier' }, + content: { type: 'string', description: 'Content text' }, + firstName: { type: 'string', description: 'First name' }, + lastName: { type: 'string', description: 'Last name' }, + emailAddress: { type: 'string', description: 'Email address' }, + backgroundInformation: { type: 'string', description: 'Background information' }, + title: { type: 'string', description: 'Task title' }, + dueDate: { type: 'string', description: 'Due date' }, }, outputs: { - note: 'any', - notes: 'any', - contact: 'any', - contacts: 'any', - task: 'any', - tasks: 'any', - metadata: 'json', - success: 'any', + note: { type: 'any', description: 'Note data' }, + notes: { type: 'any', description: 'Notes list' }, + contact: { type: 'any', description: 'Contact data' }, + contacts: { type: 'any', description: 'Contacts list' }, + task: { type: 'any', description: 'Task data' }, + tasks: { type: 'any', description: 'Tasks list' }, + metadata: { type: 'json', description: 'Operation metadata' }, + success: { type: 'any', description: 'Success status' }, }, } diff --git a/apps/sim/blocks/blocks/webhook.ts b/apps/sim/blocks/blocks/webhook.ts index f9b13366b..d04e1b550 100644 --- a/apps/sim/blocks/blocks/webhook.ts +++ b/apps/sim/blocks/blocks/webhook.ts @@ -90,7 +90,7 @@ export const WebhookBlock: BlockConfig = { access: [], // No external tools needed }, - inputs: {}, // No inputs - webhook triggers are pure input sources + inputs: {}, // No inputs - webhook triggers receive data externally outputs: {}, // No outputs - webhook data is injected directly into workflow context } diff --git a/apps/sim/blocks/blocks/whatsapp.ts b/apps/sim/blocks/blocks/whatsapp.ts index 527db0182..6d62f13af 100644 --- a/apps/sim/blocks/blocks/whatsapp.ts +++ b/apps/sim/blocks/blocks/whatsapp.ts @@ -19,6 +19,7 @@ export const WhatsAppBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter phone number with country code (e.g., +1234567890)', + required: true, }, { id: 'message', @@ -26,6 +27,7 @@ export const WhatsAppBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'Enter your message', + required: true, }, { id: 'phoneNumberId', @@ -33,6 +35,7 @@ export const WhatsAppBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Your WhatsApp Business Phone Number ID', + required: true, }, { id: 'accessToken', @@ -41,6 +44,7 @@ export const WhatsAppBlock: BlockConfig = { layout: 'full', placeholder: 'Your WhatsApp Business API Access Token', password: true, + required: true, }, ], tools: { @@ -50,14 +54,14 @@ export const WhatsAppBlock: BlockConfig = { }, }, inputs: { - phoneNumber: { type: 'string', required: true }, - message: { type: 'string', required: true }, - phoneNumberId: { type: 'string', required: true }, - accessToken: { type: 'string', required: true }, + phoneNumber: { type: 'string', description: 'Recipient phone number' }, + message: { type: 'string', description: 'Message text' }, + phoneNumberId: { type: 'string', description: 'WhatsApp phone number ID' }, + accessToken: { type: 'string', description: 'WhatsApp access token' }, }, outputs: { - success: 'boolean', - messageId: 'any', - error: 'any', + success: { type: 'boolean', description: 'Send success status' }, + messageId: { type: 'any', description: 'Message identifier' }, + error: { type: 'any', description: 'Error information' }, }, } diff --git a/apps/sim/blocks/blocks/wikipedia.ts b/apps/sim/blocks/blocks/wikipedia.ts index 31578add7..79f739d64 100644 --- a/apps/sim/blocks/blocks/wikipedia.ts +++ b/apps/sim/blocks/blocks/wikipedia.ts @@ -34,6 +34,7 @@ export const WikipediaBlock: BlockConfig = { layout: 'full', placeholder: 'Enter Wikipedia page title (e.g., "Python programming language")...', condition: { field: 'operation', value: 'wikipedia_summary' }, + required: true, }, // Search Pages operation inputs { @@ -43,6 +44,7 @@ export const WikipediaBlock: BlockConfig = { layout: 'full', placeholder: 'Enter search terms...', condition: { field: 'operation', value: 'wikipedia_search' }, + required: true, }, { id: 'searchLimit', @@ -60,6 +62,7 @@ export const WikipediaBlock: BlockConfig = { layout: 'full', placeholder: 'Enter Wikipedia page title...', condition: { field: 'operation', value: 'wikipedia_content' }, + required: true, }, ], tools: { @@ -87,22 +90,22 @@ export const WikipediaBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, + operation: { type: 'string', description: 'Operation to perform' }, // Page Summary & Content operations - pageTitle: { type: 'string', required: false }, + pageTitle: { type: 'string', description: 'Wikipedia page title' }, // Search operation - query: { type: 'string', required: false }, - searchLimit: { type: 'number', required: false }, + query: { type: 'string', description: 'Search query terms' }, + searchLimit: { type: 'number', description: 'Maximum search results' }, }, outputs: { // Page Summary output - summary: 'json', + summary: { type: 'json', description: 'Page summary data' }, // Search output - searchResults: 'json', - totalHits: 'number', + searchResults: { type: 'json', description: 'Search results data' }, + totalHits: { type: 'number', description: 'Total search hits' }, // Page Content output - content: 'json', + content: { type: 'json', description: 'Page content data' }, // Random Page output - randomPage: 'json', + randomPage: { type: 'json', description: 'Random page data' }, }, } diff --git a/apps/sim/blocks/blocks/workflow.ts b/apps/sim/blocks/blocks/workflow.ts index 24badc9c9..f72cfaffc 100644 --- a/apps/sim/blocks/blocks/workflow.ts +++ b/apps/sim/blocks/blocks/workflow.ts @@ -49,6 +49,7 @@ export const WorkflowBlock: BlockConfig = { title: 'Select Workflow', type: 'dropdown', options: getAvailableWorkflows, + required: true, }, { id: 'input', @@ -56,6 +57,7 @@ export const WorkflowBlock: BlockConfig = { type: 'short-input', placeholder: 'Select a variable to pass to the child workflow', description: 'This variable will be available as start.input in the child workflow', + required: false, }, ], tools: { @@ -64,19 +66,17 @@ export const WorkflowBlock: BlockConfig = { inputs: { workflowId: { type: 'string', - required: true, description: 'ID of the workflow to execute', }, input: { type: 'string', - required: false, description: 'Variable reference to pass to the child workflow', }, }, outputs: { - success: 'boolean', - childWorkflowName: 'string', - result: 'json', - error: 'string', + success: { type: 'boolean', description: 'Execution success status' }, + childWorkflowName: { type: 'string', description: 'Child workflow name' }, + result: { type: 'json', description: 'Workflow execution result' }, + error: { type: 'string', description: 'Error message' }, }, } diff --git a/apps/sim/blocks/blocks/x.ts b/apps/sim/blocks/blocks/x.ts index 87dd4147d..03ccba5d8 100644 --- a/apps/sim/blocks/blocks/x.ts +++ b/apps/sim/blocks/blocks/x.ts @@ -43,6 +43,7 @@ export const XBlock: BlockConfig = { layout: 'full', placeholder: "What's happening?", condition: { field: 'operation', value: 'x_write' }, + required: true, }, { id: 'replyTo', @@ -67,6 +68,7 @@ export const XBlock: BlockConfig = { layout: 'full', placeholder: 'Enter tweet ID to read', condition: { field: 'operation', value: 'x_read' }, + required: true, }, { id: 'includeReplies', @@ -87,6 +89,7 @@ export const XBlock: BlockConfig = { layout: 'full', placeholder: 'Enter search terms (supports X search operators)', condition: { field: 'operation', value: 'x_search' }, + required: true, }, { id: 'maxResults', @@ -131,6 +134,7 @@ export const XBlock: BlockConfig = { layout: 'full', placeholder: 'Enter username (without @)', condition: { field: 'operation', value: 'x_user' }, + required: true, }, ], tools: { @@ -188,30 +192,30 @@ export const XBlock: BlockConfig = { }, }, inputs: { - operation: { type: 'string', required: true }, - credential: { type: 'string', required: true }, - text: { type: 'string', required: false }, - replyTo: { type: 'string', required: false }, - mediaIds: { type: 'string', required: false }, - poll: { type: 'json', required: false }, - tweetId: { type: 'string', required: false }, - includeReplies: { type: 'boolean', required: false }, - 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 }, - username: { type: 'string', required: false }, - includeRecentTweets: { type: 'boolean', required: false }, + operation: { type: 'string', description: 'Operation to perform' }, + credential: { type: 'string', description: 'X account credential' }, + text: { type: 'string', description: 'Tweet text content' }, + replyTo: { type: 'string', description: 'Reply to tweet ID' }, + mediaIds: { type: 'string', description: 'Media identifiers' }, + poll: { type: 'json', description: 'Poll configuration' }, + tweetId: { type: 'string', description: 'Tweet identifier' }, + includeReplies: { type: 'boolean', description: 'Include replies' }, + query: { type: 'string', description: 'Search query terms' }, + maxResults: { type: 'number', description: 'Maximum search results' }, + startTime: { type: 'string', description: 'Search start time' }, + endTime: { type: 'string', description: 'Search end time' }, + sortOrder: { type: 'string', description: 'Result sort order' }, + username: { type: 'string', description: 'User profile name' }, + includeRecentTweets: { type: 'boolean', description: 'Include recent tweets' }, }, outputs: { - tweet: 'json', - replies: 'any', - context: 'any', - tweets: 'json', - includes: 'any', - meta: 'json', - user: 'json', - recentTweets: 'any', + tweet: { type: 'json', description: 'Tweet data' }, + replies: { type: 'any', description: 'Tweet replies' }, + context: { type: 'any', description: 'Tweet context' }, + tweets: { type: 'json', description: 'Tweets data' }, + includes: { type: 'any', description: 'Additional data' }, + meta: { type: 'json', description: 'Response metadata' }, + user: { type: 'json', description: 'User profile data' }, + recentTweets: { type: 'any', description: 'Recent tweets data' }, }, } diff --git a/apps/sim/blocks/blocks/youtube.ts b/apps/sim/blocks/blocks/youtube.ts index ec5fbb022..f9c8e1e87 100644 --- a/apps/sim/blocks/blocks/youtube.ts +++ b/apps/sim/blocks/blocks/youtube.ts @@ -19,6 +19,7 @@ export const YouTubeBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Enter search query', + required: true, }, { id: 'apiKey', @@ -27,6 +28,7 @@ export const YouTubeBlock: BlockConfig = { layout: 'full', placeholder: 'Enter YouTube API Key', password: true, + required: true, }, { id: 'maxResults', @@ -41,12 +43,15 @@ export const YouTubeBlock: BlockConfig = { access: ['youtube_search'], }, inputs: { - apiKey: { type: 'string', required: true }, - query: { type: 'string', required: true }, - maxResults: { type: 'number', required: false }, + apiKey: { type: 'string', description: 'The API key for the YouTube search' }, + query: { type: 'string', description: 'The query for the YouTube search' }, + maxResults: { type: 'number', description: 'The maximum number of results to return' }, }, outputs: { - items: 'json', - totalResults: 'number', + items: { type: 'json', description: 'The items returned by the YouTube search' }, + totalResults: { + type: 'number', + description: 'The total number of results returned by the YouTube search', + }, }, } diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index d1119b2ba..a307e84fe 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -26,6 +26,7 @@ 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 { HunterBlock } from '@/blocks/blocks/hunter' import { ImageGeneratorBlock } from '@/blocks/blocks/image_generator' import { JinaBlock } from '@/blocks/blocks/jina' import { JiraBlock } from '@/blocks/blocks/jira' @@ -95,6 +96,7 @@ export const registry: Record = { google_search: GoogleSearchBlock, google_sheets: GoogleSheetsBlock, huggingface: HuggingFaceBlock, + hunter: HunterBlock, image_generator: ImageGeneratorBlock, jina: JinaBlock, jira: JiraBlock, diff --git a/apps/sim/blocks/types.ts b/apps/sim/blocks/types.ts index 5d16a1d9e..d1b3c96c7 100644 --- a/apps/sim/blocks/types.ts +++ b/apps/sim/blocks/types.ts @@ -76,10 +76,14 @@ export type BlockOutput = | PrimitiveValueType | { [key: string]: PrimitiveValueType | Record } +// Output field definition with optional description +export type OutputFieldDefinition = + | PrimitiveValueType + | { type: PrimitiveValueType; description?: string } + // Parameter validation rules export interface ParamConfig { type: ParamType - required: boolean description?: string schema?: { type: string @@ -102,6 +106,7 @@ export interface SubBlockConfig { type: SubBlockType layout?: SubBlockLayout mode?: 'basic' | 'advanced' | 'both' // Default is 'both' if not specified + required?: boolean options?: | { label: string; id: string; icon?: React.ComponentType<{ className?: string }> }[] | (() => { label: string; id: string; icon?: React.ComponentType<{ className?: string }> }[]) @@ -173,7 +178,7 @@ export interface BlockConfig { } } inputs: Record - outputs: ToolOutputToValueType> & { + outputs: Record & { visualization?: { type: 'image' url: string diff --git a/apps/sim/blocks/utils.ts b/apps/sim/blocks/utils.ts index 13dc29b5e..5d4fef147 100644 --- a/apps/sim/blocks/utils.ts +++ b/apps/sim/blocks/utils.ts @@ -1,13 +1,18 @@ -import type { BlockOutput } from '@/blocks/types' +import type { BlockOutput, OutputFieldDefinition } from '@/blocks/types' export function resolveOutputType( - outputs: Record + outputs: Record ): Record { const resolvedOutputs: Record = {} for (const [key, outputType] of Object.entries(outputs)) { - // Since dependsOn has been removed, just use the type directly - resolvedOutputs[key] = outputType as BlockOutput + // Handle new format: { type: 'string', description: '...' } + if (typeof outputType === 'object' && outputType !== null && 'type' in outputType) { + resolvedOutputs[key] = outputType.type as BlockOutput + } else { + // Handle old format: just the type as string, or other object formats + resolvedOutputs[key] = outputType as BlockOutput + } } return resolvedOutputs diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 9bd12b5af..42259f130 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -3163,3 +3163,21 @@ export function WikipediaIcon(props: SVGProps) { ) } + +export function HunterIOIcon(props: SVGProps) { + return ( + + + + ) +} diff --git a/apps/sim/components/ui/tag-dropdown.test.tsx b/apps/sim/components/ui/tag-dropdown.test.tsx index 1af14fe9c..2889f7e13 100644 --- a/apps/sim/components/ui/tag-dropdown.test.tsx +++ b/apps/sim/components/ui/tag-dropdown.test.tsx @@ -1,4 +1,4 @@ -import { describe, expect, test, vi } from 'vitest' +import { describe, expect, vi } from 'vitest' import { checkTagTrigger } from '@/components/ui/tag-dropdown' import { extractFieldsFromSchema, parseResponseFormatSafely } from '@/lib/response-format' import type { BlockState } from '@/stores/workflows/workflow/types' @@ -35,7 +35,7 @@ vi.mock('@/stores/workflows/subblock/store', () => ({ })) describe('TagDropdown Loop Suggestions', () => { - test('should generate correct loop suggestions for forEach loops', () => { + it.concurrent('should generate correct loop suggestions for forEach loops', () => { const blocks: Record = { loop1: { id: 'loop1', @@ -99,7 +99,7 @@ describe('TagDropdown Loop Suggestions', () => { expect(loopTags).toHaveLength(3) }) - test('should only generate loop.index for regular for loops', () => { + it.concurrent('should only generate loop.index for regular for loops', () => { const blocks: Record = { loop1: { id: 'loop1', @@ -158,7 +158,7 @@ describe('TagDropdown Loop Suggestions', () => { }) describe('TagDropdown Parallel Suggestions', () => { - test('should generate correct parallel suggestions', () => { + it.concurrent('should generate correct parallel suggestions', () => { const blocks: Record = { parallel1: { id: 'parallel1', @@ -217,7 +217,7 @@ describe('TagDropdown Parallel Suggestions', () => { }) describe('TagDropdown Variable Suggestions', () => { - test('should generate variable tags with correct format', () => { + it.concurrent('should generate variable tags with correct format', () => { const variables = [ { id: 'var1', name: 'User Name', type: 'string' }, { id: 'var2', name: 'User Age', type: 'number' }, @@ -232,7 +232,7 @@ describe('TagDropdown Variable Suggestions', () => { expect(variableTags).toEqual(['variable.UserName', 'variable.UserAge', 'variable.IsActive']) }) - test('should create variable info map correctly', () => { + it.concurrent('should create variable info map correctly', () => { const variables = [ { id: 'var1', name: 'User Name', type: 'string' }, { id: 'var2', name: 'User Age', type: 'number' }, @@ -259,7 +259,7 @@ describe('TagDropdown Variable Suggestions', () => { }) describe('TagDropdown Search and Filtering', () => { - test('should extract search term from input correctly', () => { + it.concurrent('should extract search term from input correctly', () => { const testCases = [ { input: 'Hello { }) }) - test('should filter tags based on search term', () => { + it.concurrent('should filter tags based on search term', () => { const tags = [ 'variable.userName', 'variable.userAge', @@ -293,7 +293,7 @@ describe('TagDropdown Search and Filtering', () => { expect(filteredTags).toEqual(['variable.userName', 'variable.userAge']) }) - test('should group tags correctly by type', () => { + it.concurrent('should group tags correctly by type', () => { const tags = [ 'variable.userName', 'loop.index', @@ -328,7 +328,7 @@ describe('TagDropdown Search and Filtering', () => { }) describe('checkTagTrigger helper function', () => { - test('should return true when there is an unclosed < bracket', () => { + it.concurrent('should return true when there is an unclosed < bracket', () => { const testCases = [ { text: 'Hello <', cursorPosition: 7, expected: true }, { text: 'Hello { }) }) - test('should return false when there is no unclosed < bracket', () => { + it.concurrent('should return false when there is no unclosed < bracket', () => { const testCases = [ { text: 'Hello world', cursorPosition: 11, expected: false }, { text: 'Hello ', cursorPosition: 11, expected: false }, @@ -355,7 +355,7 @@ describe('checkTagTrigger helper function', () => { }) }) - test('should handle edge cases correctly', () => { + it.concurrent('should handle edge cases correctly', () => { // Cursor at position 0 expect(checkTagTrigger('Hello', 0).show).toBe(false) @@ -368,7 +368,7 @@ describe('checkTagTrigger helper function', () => { }) describe('extractFieldsFromSchema helper function logic', () => { - test('should extract fields from JSON Schema format', () => { + it.concurrent('should extract fields from JSON Schema format', () => { const responseFormat = { schema: { properties: { @@ -388,7 +388,7 @@ describe('extractFieldsFromSchema helper function logic', () => { ]) }) - test('should handle direct schema format', () => { + it.concurrent('should handle direct schema format', () => { const responseFormat = { properties: { status: { type: 'boolean', description: 'Status flag' }, @@ -404,7 +404,7 @@ describe('extractFieldsFromSchema helper function logic', () => { ]) }) - test('should return empty array for invalid or missing schema', () => { + it.concurrent('should return empty array for invalid or missing schema', () => { expect(extractFieldsFromSchema(null)).toEqual([]) expect(extractFieldsFromSchema(undefined)).toEqual([]) expect(extractFieldsFromSchema({})).toEqual([]) @@ -413,7 +413,7 @@ describe('extractFieldsFromSchema helper function logic', () => { expect(extractFieldsFromSchema('invalid')).toEqual([]) }) - test('should handle array properties correctly', () => { + it.concurrent('should handle array properties correctly', () => { const responseFormat = { properties: { items: ['string', 'array'], @@ -429,7 +429,7 @@ describe('extractFieldsFromSchema helper function logic', () => { ]) }) - test('should default to string type when type is missing', () => { + it.concurrent('should default to string type when type is missing', () => { const responseFormat = { properties: { name: { description: 'User name' }, @@ -445,7 +445,7 @@ describe('extractFieldsFromSchema helper function logic', () => { ]) }) - test('should handle flattened response format (new format)', () => { + it.concurrent('should handle flattened response format (new format)', () => { const responseFormat = { schema: { properties: { @@ -467,7 +467,7 @@ describe('extractFieldsFromSchema helper function logic', () => { }) describe('TagDropdown Tag Ordering', () => { - test('should create ordered tags array in correct sequence', () => { + it.concurrent('should create ordered tags array in correct sequence', () => { const variableTags = ['variable.userName', 'variable.userAge'] const loopTags = ['loop.index', 'loop.currentItem'] const parallelTags = ['parallel.index'] @@ -485,7 +485,7 @@ describe('TagDropdown Tag Ordering', () => { ]) }) - test('should create tag index map correctly', () => { + it.concurrent('should create tag index map correctly', () => { const orderedTags = ['variable.userName', 'loop.index', 'block.data'] const tagIndexMap = new Map() @@ -501,7 +501,7 @@ describe('TagDropdown Tag Ordering', () => { }) describe('TagDropdown Tag Selection Logic', () => { - test('should handle existing closing bracket correctly when editing tags', () => { + it.concurrent('should handle existing closing bracket correctly when editing tags', () => { const testCases = [ { description: 'should remove existing closing bracket from incomplete tag', @@ -564,7 +564,7 @@ describe('TagDropdown Tag Selection Logic', () => { }) }) - test('should validate tag-like character regex correctly', () => { + it.concurrent('should validate tag-like character regex correctly', () => { const regex = /^[a-zA-Z0-9._]*$/ // Valid tag-like text @@ -583,7 +583,7 @@ describe('TagDropdown Tag Selection Logic', () => { expect(regex.test('content.data!')).toBe(false) // exclamation }) - test('should find correct position of last open bracket', () => { + it.concurrent('should find correct position of last open bracket', () => { const testCases = [ { input: 'Hello and { }) }) - test('should find correct position of next closing bracket', () => { + it.concurrent('should find correct position of next closing bracket', () => { const testCases = [ { input: 'input>', expected: 5 }, { input: 'content.data> more text', expected: 12 }, diff --git a/apps/sim/components/ui/tag-dropdown.tsx b/apps/sim/components/ui/tag-dropdown.tsx index 77d371f2b..2eb9f5f2a 100644 --- a/apps/sim/components/ui/tag-dropdown.tsx +++ b/apps/sim/components/ui/tag-dropdown.tsx @@ -57,9 +57,15 @@ const generateOutputPaths = (outputs: Record, prefix = ''): string[ // Simple type like 'string', 'number', 'json', 'any' paths.push(currentPath) } else if (typeof value === 'object' && value !== null) { - // Nested object - recurse - const subPaths = generateOutputPaths(value, currentPath) - paths.push(...subPaths) + // Check if this is our new format with type and description + if ('type' in value && typeof value.type === 'string') { + // New format: { type: 'string', description: '...' } - treat as leaf node + paths.push(currentPath) + } else { + // Legacy nested object - recurse + const subPaths = generateOutputPaths(value, currentPath) + paths.push(...subPaths) + } } else { // Fallback - add the path paths.push(currentPath) diff --git a/apps/sim/executor/__test-utils__/mock-dependencies.ts b/apps/sim/executor/__test-utils__/mock-dependencies.ts index ed47fa99b..2d3939ded 100644 --- a/apps/sim/executor/__test-utils__/mock-dependencies.ts +++ b/apps/sim/executor/__test-utils__/mock-dependencies.ts @@ -21,12 +21,13 @@ vi.mock('@/blocks/index', () => ({ vi.mock('@/tools/utils', () => ({ getTool: vi.fn(), getToolAsync: vi.fn(), - validateToolRequest: vi.fn(), + validateToolRequest: vi.fn(), // Keep for backward compatibility formatRequestParams: vi.fn(), transformTable: vi.fn(), createParamSchema: vi.fn(), getClientEnvVars: vi.fn(), createCustomToolRequestBody: vi.fn(), + validateRequiredParametersAfterMerge: vi.fn(), })) // Utils diff --git a/apps/sim/executor/handlers/workflow/workflow-handler.ts b/apps/sim/executor/handlers/workflow/workflow-handler.ts index 1de48a9b4..9a85cf5cf 100644 --- a/apps/sim/executor/handlers/workflow/workflow-handler.ts +++ b/apps/sim/executor/handlers/workflow/workflow-handler.ts @@ -176,7 +176,8 @@ export class WorkflowBlockHandler implements BlockHandler { workflowState.blocks, workflowState.edges || [], workflowState.loops || {}, - workflowState.parallels || {} + workflowState.parallels || {}, + true // Enable validation during execution ) const workflowVariables = (workflowData.variables as Record) || {} diff --git a/apps/sim/executor/index.test.ts b/apps/sim/executor/index.test.ts index e9f838ecc..d273ebf8a 100644 --- a/apps/sim/executor/index.test.ts +++ b/apps/sim/executor/index.test.ts @@ -7,7 +7,7 @@ * running workflow blocks in topological order, handling the execution flow, * resolving inputs and dependencies, and managing errors. */ -import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' +import { afterEach, beforeEach, describe, expect, vi } from 'vitest' import { Executor } from '@/executor' import { createMinimalWorkflow, @@ -56,7 +56,7 @@ describe('Executor', () => { * Initialization tests */ describe('initialization', () => { - test('should create an executor instance with legacy constructor format', () => { + it.concurrent('should create an executor instance with legacy constructor format', () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) @@ -64,7 +64,7 @@ describe('Executor', () => { expect(executor).toBeInstanceOf(Executor) }) - test('should create an executor instance with new options object format', () => { + it.concurrent('should create an executor instance with new options object format', () => { const workflow = createMinimalWorkflow() const initialStates = { block1: { result: { value: 'Initial state' } }, @@ -92,7 +92,7 @@ describe('Executor', () => { expect((executor as any).workflowVariables).toEqual(workflowVariables) }) - test('should accept streaming context extensions', () => { + it.concurrent('should accept streaming context extensions', () => { const workflow = createMinimalWorkflow() const mockOnStream = vi.fn() @@ -109,7 +109,7 @@ describe('Executor', () => { expect(executor).toBeDefined() }) - test('should handle legacy constructor with individual parameters', () => { + it.concurrent('should handle legacy constructor with individual parameters', () => { const workflow = createMinimalWorkflow() const initialStates = { block1: { result: { value: 'Initial state' } }, @@ -133,7 +133,7 @@ describe('Executor', () => { * Validation tests */ describe('workflow validation', () => { - test('should validate workflow on initialization', () => { + it.concurrent('should validate workflow on initialization', () => { const validateSpy = vi.spyOn(Executor.prototype as any, 'validateWorkflow') const workflow = createMinimalWorkflow() @@ -142,7 +142,7 @@ describe('Executor', () => { expect(validateSpy).toHaveBeenCalled() }) - test('should validate workflow on execution', async () => { + it('should validate workflow on execution', async () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) @@ -154,21 +154,21 @@ describe('Executor', () => { expect(validateSpy).toHaveBeenCalledTimes(1) }) - test('should throw error for workflow without starter block', () => { + it.concurrent('should throw error for workflow without starter block', () => { const workflow = createMinimalWorkflow() workflow.blocks = workflow.blocks.filter((block) => block.metadata?.id !== BlockType.STARTER) expect(() => new Executor(workflow)).toThrow('Workflow must have an enabled starter block') }) - test('should throw error for workflow with disabled starter block', () => { + it.concurrent('should throw error for workflow with disabled starter block', () => { const workflow = createMinimalWorkflow() workflow.blocks.find((block) => block.metadata?.id === BlockType.STARTER)!.enabled = false expect(() => new Executor(workflow)).toThrow('Workflow must have an enabled starter block') }) - test('should throw error if starter block has incoming connections', () => { + it.concurrent('should throw error if starter block has incoming connections', () => { const workflow = createMinimalWorkflow() workflow.connections.push({ source: 'block1', @@ -178,7 +178,7 @@ describe('Executor', () => { expect(() => new Executor(workflow)).toThrow('Starter block cannot have incoming connections') }) - test('should throw error if starter block has no outgoing connections', () => { + it.concurrent('should throw error if starter block has no outgoing connections', () => { const workflow = createMinimalWorkflow() workflow.connections = [] @@ -187,7 +187,7 @@ describe('Executor', () => { ) }) - test('should throw error if connection references non-existent source block', () => { + it.concurrent('should throw error if connection references non-existent source block', () => { const workflow = createMinimalWorkflow() workflow.connections.push({ source: 'non-existent-block', @@ -199,7 +199,7 @@ describe('Executor', () => { ) }) - test('should throw error if connection references non-existent target block', () => { + it.concurrent('should throw error if connection references non-existent target block', () => { const workflow = createMinimalWorkflow() workflow.connections.push({ source: 'starter', @@ -216,7 +216,7 @@ describe('Executor', () => { * Execution tests */ describe('workflow execution', () => { - test('should execute workflow and return ExecutionResult', async () => { + it.concurrent('should execute workflow and return ExecutionResult', async () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) @@ -238,7 +238,7 @@ describe('Executor', () => { } }) - test('should handle streaming execution with onStream callback', async () => { + it.concurrent('should handle streaming execution with onStream callback', async () => { const workflow = createMinimalWorkflow() const mockOnStream = vi.fn() @@ -263,7 +263,7 @@ describe('Executor', () => { } }) - test('should pass context extensions to execution context', async () => { + it.concurrent('should pass context extensions to execution context', async () => { const workflow = createMinimalWorkflow() const mockOnStream = vi.fn() const selectedOutputIds = ['block1', 'block2'] @@ -293,7 +293,7 @@ describe('Executor', () => { * Condition and loop tests */ describe('special blocks', () => { - test('should handle condition blocks without errors', async () => { + it.concurrent('should handle condition blocks without errors', async () => { const workflow = createWorkflowWithCondition() const executor = new Executor(workflow) @@ -309,7 +309,7 @@ describe('Executor', () => { } }) - test('should handle loop structures without errors', async () => { + it.concurrent('should handle loop structures without errors', async () => { const workflow = createWorkflowWithLoop() const executor = new Executor(workflow) @@ -330,7 +330,7 @@ describe('Executor', () => { * Debug mode tests */ describe('debug mode', () => { - test('should detect debug mode from settings', async () => { + it('should detect debug mode from settings', async () => { // Reset and reconfigure mocks for debug mode vi.resetModules() vi.clearAllMocks() @@ -348,7 +348,7 @@ describe('Executor', () => { expect(isDebugging).toBe(true) }) - test('should work with debug mode disabled', async () => { + it.concurrent('should work with debug mode disabled', async () => { // Reset and reconfigure mocks for normal mode vi.resetModules() vi.clearAllMocks() @@ -366,7 +366,7 @@ describe('Executor', () => { expect(isDebugging).toBe(false) }) - test('should handle continue execution in debug mode', async () => { + it.concurrent('should handle continue execution in debug mode', async () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) @@ -390,7 +390,7 @@ describe('Executor', () => { * Additional tests to improve coverage */ describe('block output handling', () => { - test('should handle different block outputs correctly', () => { + it.concurrent('should handle different block outputs correctly', () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) @@ -399,7 +399,7 @@ describe('Executor', () => { expect(typeof executor.execute).toBe('function') }) - test('should handle error outputs correctly', () => { + it.concurrent('should handle error outputs correctly', () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) @@ -417,7 +417,7 @@ describe('Executor', () => { * Error handling tests */ describe('error handling', () => { - test('should activate error paths when a block has an error', () => { + it.concurrent('should activate error paths when a block has an error', () => { const workflow = createWorkflowWithErrorPath() const executor = new Executor(workflow) @@ -448,7 +448,7 @@ describe('Executor', () => { expect(context.activeExecutionPath.has('error-handler')).toBe(true) }) - test('should not activate error paths for starter and condition blocks', () => { + it.concurrent('should not activate error paths for starter and condition blocks', () => { const workflow = createWorkflowWithErrorPath() const executor = new Executor(workflow) @@ -490,7 +490,7 @@ describe('Executor', () => { expect(activateErrorPath('condition-block', context)).toBe(false) }) - test('should return false if no error connections exist', () => { + it.concurrent('should return false if no error connections exist', () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) @@ -516,7 +516,7 @@ describe('Executor', () => { expect(result).toBe(false) }) - test('should create proper error output for a block error', () => { + it.concurrent('should create proper error output for a block error', () => { const workflow = createWorkflowWithErrorPath() const executor = new Executor(workflow) @@ -553,7 +553,7 @@ describe('Executor', () => { expect(errorOutput).toHaveProperty('status') }) - test('should handle "undefined (undefined)" error case', () => { + it.concurrent('should handle "undefined (undefined)" error case', () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) @@ -571,7 +571,7 @@ describe('Executor', () => { * Streaming execution tests */ describe('streaming execution', () => { - test('should handle streaming execution results', async () => { + it.concurrent('should handle streaming execution results', async () => { const workflow = createMinimalWorkflow() const mockOnStream = vi.fn() @@ -608,7 +608,7 @@ describe('Executor', () => { } }) - test('should process streaming content in context', async () => { + it.concurrent('should process streaming content in context', async () => { const workflow = createMinimalWorkflow() const mockOnStream = vi.fn() @@ -634,7 +634,7 @@ describe('Executor', () => { * Dependency checking logic tests */ describe('dependency checking', () => { - test('should handle multi-input blocks with inactive sources correctly', () => { + it.concurrent('should handle multi-input blocks with inactive sources correctly', () => { // Create workflow with router -> multiple APIs -> single agent const routerWorkflow = { version: '1.0', @@ -726,7 +726,7 @@ describe('Executor', () => { expect(dependenciesMet).toBe(true) }) - test('should prioritize special connection types over active path check', () => { + it.concurrent('should prioritize special connection types over active path check', () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) const checkDependencies = (executor as any).checkDependencies.bind(executor) @@ -763,7 +763,7 @@ describe('Executor', () => { expect(loopDepsResult).toBe(true) // loop completed = dependency met }) - test('should handle router decisions correctly in dependency checking', () => { + it.concurrent('should handle router decisions correctly in dependency checking', () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) const checkDependencies = (executor as any).checkDependencies.bind(executor) @@ -808,7 +808,7 @@ describe('Executor', () => { expect(nonSelectedResult).toBe(false) // router executed + target NOT selected = dependency NOT met }) - test('should handle condition decisions correctly in dependency checking', () => { + it.concurrent('should handle condition decisions correctly in dependency checking', () => { const conditionWorkflow = createWorkflowWithCondition() const executor = new Executor(conditionWorkflow) const checkDependencies = (executor as any).checkDependencies.bind(executor) @@ -840,7 +840,7 @@ describe('Executor', () => { expect(falseResult).toBe(true) // unselected condition paths are treated as "not applicable" to support multi-path scenarios }) - test('should handle regular sequential dependencies correctly', () => { + it.concurrent('should handle regular sequential dependencies correctly', () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) const checkDependencies = (executor as any).checkDependencies.bind(executor) @@ -869,7 +869,7 @@ describe('Executor', () => { expect(errorResult).toBe(false) // source executed + has error = regular dependency not met }) - test('should handle empty dependency list', () => { + it.concurrent('should handle empty dependency list', () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) const checkDependencies = (executor as any).checkDependencies.bind(executor) @@ -887,7 +887,7 @@ describe('Executor', () => { * Cancellation tests */ describe('workflow cancellation', () => { - test('should set cancellation flag when cancel() is called', () => { + it.concurrent('should set cancellation flag when cancel() is called', () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) @@ -899,7 +899,7 @@ describe('Executor', () => { expect((executor as any).isCancelled).toBe(true) }) - test('should handle cancellation in debug mode continueExecution', async () => { + it.concurrent('should handle cancellation in debug mode continueExecution', async () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) @@ -920,7 +920,7 @@ describe('Executor', () => { expect(result.error).toBe('Workflow execution was cancelled') }) - test('should handle multiple cancel() calls gracefully', () => { + it.concurrent('should handle multiple cancel() calls gracefully', () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) @@ -932,7 +932,7 @@ describe('Executor', () => { expect((executor as any).isCancelled).toBe(true) }) - test('should prevent new execution on cancelled executor', async () => { + it.concurrent('should prevent new execution on cancelled executor', async () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) @@ -949,7 +949,7 @@ describe('Executor', () => { } }) - test('should return cancelled result when cancellation flag is checked', async () => { + it.concurrent('should return cancelled result when cancellation flag is checked', async () => { const workflow = createMinimalWorkflow() const executor = new Executor(workflow) diff --git a/apps/sim/serializer/index.test.ts b/apps/sim/serializer/index.test.ts index 348715aa7..0c9527265 100644 --- a/apps/sim/serializer/index.test.ts +++ b/apps/sim/serializer/index.test.ts @@ -7,7 +7,7 @@ * converting between workflow state (blocks, edges, loops) and serialized format * used by the executor. */ -import { describe, expect, test, vi } from 'vitest' +import { describe, expect, vi } from 'vitest' import { getProviderFromModel } from '@/providers/utils' import { createAgentWithToolsWorkflowState, @@ -22,7 +22,6 @@ import { import { Serializer } from '@/serializer/index' import type { SerializedWorkflow } from '@/serializer/types' -// Mock getBlock function vi.mock('@/blocks', () => ({ getBlock: (type: string) => { // Mock block configurations for different block types @@ -120,12 +119,76 @@ vi.mock('@/blocks', () => ({ ], inputs: {}, }, + jina: { + name: 'Jina', + description: 'Convert website content into text', + category: 'tools', + bgColor: '#333333', + tools: { + access: ['jina_read_url'], + config: { + tool: () => 'jina_read_url', + }, + }, + subBlocks: [ + { id: 'url', type: 'short-input', title: 'URL', required: true }, + { id: 'apiKey', type: 'short-input', title: 'API Key', required: true }, + ], + inputs: { + url: { type: 'string' }, + apiKey: { type: 'string' }, + }, + }, + reddit: { + name: 'Reddit', + description: 'Access Reddit data and content', + category: 'tools', + bgColor: '#FF5700', + tools: { + access: ['reddit_get_posts', 'reddit_get_comments'], + config: { + tool: () => 'reddit_get_posts', + }, + }, + subBlocks: [ + { id: 'operation', type: 'dropdown', title: 'Operation', required: true }, + { id: 'credential', type: 'oauth-input', title: 'Reddit Account', required: true }, + { id: 'subreddit', type: 'short-input', title: 'Subreddit', required: true }, + ], + inputs: { + operation: { type: 'string' }, + credential: { type: 'string' }, + subreddit: { type: 'string' }, + }, + }, } return mockConfigs[type] || null }, })) +// Mock getTool function +vi.mock('@/tools/utils', () => ({ + getTool: (toolId: string) => { + // Mock tool configurations for testing + const mockTools: Record = { + jina_read_url: { + params: { + url: { visibility: 'user-or-llm', required: true }, + apiKey: { visibility: 'user-only', required: true }, + }, + }, + reddit_get_posts: { + params: { + subreddit: { visibility: 'user-or-llm', required: true }, + credential: { visibility: 'user-only', required: true }, + }, + }, + } + return mockTools[toolId] || null + }, +})) + // Mock logger vi.mock('@/lib/logs/console/logger', () => ({ createLogger: () => ({ @@ -141,7 +204,7 @@ describe('Serializer', () => { * Serialization tests */ describe('serializeWorkflow', () => { - test('should serialize a minimal workflow correctly', () => { + it.concurrent('should serialize a minimal workflow correctly', () => { const { blocks, edges, loops } = createMinimalWorkflowState() const serializer = new Serializer() @@ -170,7 +233,7 @@ describe('Serializer', () => { expect(serialized.connections[0].target).toBe('agent1') }) - test('should serialize a conditional workflow correctly', () => { + it.concurrent('should serialize a conditional workflow correctly', () => { const { blocks, edges, loops } = createConditionalWorkflowState() const serializer = new Serializer() @@ -202,7 +265,7 @@ describe('Serializer', () => { expect(falsePathConnection?.target).toBe('agent2') }) - test('should serialize a workflow with loops correctly', () => { + it.concurrent('should serialize a workflow with loops correctly', () => { const { blocks, edges, loops } = createLoopWorkflowState() const serializer = new Serializer() @@ -223,7 +286,7 @@ describe('Serializer', () => { expect(loopBackConnection?.sourceHandle).toBe('condition-true') }) - test('should serialize a complex workflow with multiple block types', () => { + it.concurrent('should serialize a complex workflow with multiple block types', () => { const { blocks, edges, loops } = createComplexWorkflowState() const serializer = new Serializer() @@ -260,7 +323,7 @@ describe('Serializer', () => { expect(agentBlock?.outputs.responseFormat).toBeDefined() }) - test('should serialize agent block with custom tools correctly', () => { + it.concurrent('should serialize agent block with custom tools correctly', () => { const { blocks, edges, loops } = createAgentWithToolsWorkflowState() const serializer = new Serializer() @@ -292,7 +355,7 @@ describe('Serializer', () => { expect(functionTool.name).toBe('calculator') }) - test('should handle invalid block types gracefully', () => { + it.concurrent('should handle invalid block types gracefully', () => { const { blocks, edges, loops } = createInvalidWorkflowState() const serializer = new Serializer() @@ -307,7 +370,7 @@ describe('Serializer', () => { * Deserialization tests */ describe('deserializeWorkflow', () => { - test('should deserialize a serialized workflow correctly', () => { + it.concurrent('should deserialize a serialized workflow correctly', () => { const { blocks, edges, loops } = createMinimalWorkflowState() const serializer = new Serializer() @@ -341,7 +404,7 @@ describe('Serializer', () => { expect(deserialized.edges[0].target).toBe('agent1') }) - test('should deserialize a complex workflow with all block types', () => { + it.concurrent('should deserialize a complex workflow with all block types', () => { const { blocks, edges, loops } = createComplexWorkflowState() const serializer = new Serializer() @@ -379,7 +442,7 @@ describe('Serializer', () => { expect(agentBlock.subBlocks.provider.value).toBe('openai') }) - test('should handle serialized workflow with invalid block metadata', () => { + it.concurrent('should handle serialized workflow with invalid block metadata', () => { const invalidWorkflow = createInvalidSerializedWorkflow() as SerializedWorkflow const serializer = new Serializer() @@ -389,7 +452,7 @@ describe('Serializer', () => { ) }) - test('should handle serialized workflow with missing metadata', () => { + it.concurrent('should handle serialized workflow with missing metadata', () => { const invalidWorkflow = createMissingMetadataWorkflow() as SerializedWorkflow const serializer = new Serializer() @@ -402,7 +465,7 @@ describe('Serializer', () => { * End-to-end serialization/deserialization tests */ describe('round-trip serialization', () => { - test('should preserve all data through serialization and deserialization', () => { + it.concurrent('should preserve all data through serialization and deserialization', () => { const { blocks, edges, loops } = createComplexWorkflowState() const serializer = new Serializer() @@ -446,4 +509,211 @@ describe('Serializer', () => { expect(reserialized.loops).toEqual(serialized.loops) }) }) + + describe('validation during serialization', () => { + it.concurrent('should throw error for missing user-only required fields', () => { + const serializer = new Serializer() + + // Create a block state with a missing user-only required field (API key) + const blockWithMissingUserOnlyField: any = { + id: 'test-block', + type: 'jina', + name: 'Test Jina Block', + position: { x: 0, y: 0 }, + subBlocks: { + url: { value: 'https://example.com' }, + apiKey: { value: null }, // Missing user-only required field + }, + outputs: {}, + enabled: true, + } + + expect(() => { + serializer.serializeWorkflow( + { 'test-block': blockWithMissingUserOnlyField }, + [], + {}, + undefined, + true + ) + }).toThrow('Test Jina Block is missing required fields: API Key') + }) + + it.concurrent('should not throw error when all user-only required fields are present', () => { + const serializer = new Serializer() + + const blockWithAllUserOnlyFields: any = { + id: 'test-block', + type: 'jina', + name: 'Test Jina Block', + position: { x: 0, y: 0 }, + subBlocks: { + url: { value: 'https://example.com' }, + apiKey: { value: 'test-api-key' }, + }, + outputs: {}, + enabled: true, + } + + expect(() => { + serializer.serializeWorkflow( + { 'test-block': blockWithAllUserOnlyFields }, + [], + {}, + undefined, + true + ) + }).not.toThrow() + }) + + it.concurrent('should not validate user-or-llm fields during serialization', () => { + const serializer = new Serializer() + + // Create a Reddit block with missing subreddit (user-or-llm field) + const blockWithMissingUserOrLlmField: any = { + id: 'test-block', + type: 'reddit', + name: 'Test Reddit Block', + position: { x: 0, y: 0 }, + subBlocks: { + operation: { value: 'get_posts' }, + credential: { value: 'test-credential' }, + subreddit: { value: null }, // Missing user-or-llm field - should NOT be validated here + }, + outputs: {}, + enabled: true, + } + + // Should NOT throw because subreddit is user-or-llm, not user-only + expect(() => { + serializer.serializeWorkflow( + { 'test-block': blockWithMissingUserOrLlmField }, + [], + {}, + undefined, + true + ) + }).not.toThrow() + }) + + it.concurrent('should not validate when validateRequired is false', () => { + const serializer = new Serializer() + + const blockWithMissingField: any = { + id: 'test-block', + type: 'jina', + name: 'Test Jina Block', + position: { x: 0, y: 0 }, + subBlocks: { + url: { value: 'https://example.com' }, + apiKey: { value: null }, // Missing required field + }, + outputs: {}, + enabled: true, + } + + // Should NOT throw when validation is disabled (default behavior) + expect(() => { + serializer.serializeWorkflow({ 'test-block': blockWithMissingField }, [], {}) + }).not.toThrow() + }) + + it.concurrent('should validate multiple user-only fields and report all missing', () => { + const serializer = new Serializer() + + const blockWithMultipleMissing: any = { + id: 'test-block', + type: 'jina', + name: 'Test Jina Block', + position: { x: 0, y: 0 }, + subBlocks: { + url: { value: null }, // Missing user-or-llm field (should NOT be validated) + apiKey: { value: null }, // Missing user-only field (should be validated) + }, + outputs: {}, + enabled: true, + } + + expect(() => { + serializer.serializeWorkflow( + { 'test-block': blockWithMultipleMissing }, + [], + {}, + undefined, + true + ) + }).toThrow('Test Jina Block is missing required fields: API Key') + }) + + it.concurrent('should handle blocks with no tool configuration gracefully', () => { + const serializer = new Serializer() + + const blockWithNoTools: any = { + id: 'test-block', + type: 'condition', // Condition blocks have different tool setup + name: 'Test Condition Block', + position: { x: 0, y: 0 }, + subBlocks: { + condition: { value: null }, // Missing required field but not user-only + }, + outputs: {}, + enabled: true, + } + + // Should NOT throw because condition blocks don't have user-only params + expect(() => { + serializer.serializeWorkflow({ 'test-block': blockWithNoTools }, [], {}, undefined, true) + }).not.toThrow() + }) + + it.concurrent('should handle empty string values as missing', () => { + const serializer = new Serializer() + + const blockWithEmptyString: any = { + id: 'test-block', + type: 'jina', + name: 'Test Jina Block', + position: { x: 0, y: 0 }, + subBlocks: { + url: { value: 'https://example.com' }, + apiKey: { value: '' }, // Empty string should be treated as missing + }, + outputs: {}, + enabled: true, + } + + expect(() => { + serializer.serializeWorkflow( + { 'test-block': blockWithEmptyString }, + [], + {}, + undefined, + true + ) + }).toThrow('Test Jina Block is missing required fields: API Key') + }) + + it.concurrent('should only validate user-only fields, not user-or-llm fields', () => { + const serializer = new Serializer() + + // Block with both user-only and user-or-llm missing fields + const mixedBlock: any = { + id: 'test-block', + type: 'reddit', + name: 'Test Reddit Block', + position: { x: 0, y: 0 }, + subBlocks: { + operation: { value: 'get_posts' }, + credential: { value: null }, // user-only - should be validated + subreddit: { value: null }, // user-or-llm - should NOT be validated + }, + outputs: {}, + enabled: true, + } + + expect(() => { + serializer.serializeWorkflow({ 'test-block': mixedBlock }, [], {}, undefined, true) + }).toThrow('Test Reddit Block is missing required fields: Reddit Account') + }) + }) }) diff --git a/apps/sim/serializer/index.ts b/apps/sim/serializer/index.ts index 9bf9cbdc0..73cf46f10 100644 --- a/apps/sim/serializer/index.ts +++ b/apps/sim/serializer/index.ts @@ -3,6 +3,7 @@ import { createLogger } from '@/lib/logs/console/logger' import { getBlock } from '@/blocks' import type { SerializedBlock, SerializedWorkflow } from '@/serializer/types' import type { BlockState, Loop, Parallel } from '@/stores/workflows/workflow/types' +import { getTool } from '@/tools/utils' const logger = createLogger('Serializer') @@ -11,11 +12,12 @@ export class Serializer { blocks: Record, edges: Edge[], loops: Record, - parallels?: Record + parallels?: Record, + validateRequired = false ): SerializedWorkflow { return { version: '1.0', - blocks: Object.values(blocks).map((block) => this.serializeBlock(block)), + blocks: Object.values(blocks).map((block) => this.serializeBlock(block, validateRequired)), connections: edges.map((edge) => ({ source: edge.source, target: edge.target, @@ -27,7 +29,7 @@ export class Serializer { } } - private serializeBlock(block: BlockState): SerializedBlock { + private serializeBlock(block: BlockState, validateRequired = false): SerializedBlock { // Special handling for subflow blocks (loops, parallels, etc.) if (block.type === 'loop' || block.type === 'parallel') { return { @@ -55,8 +57,14 @@ export class Serializer { throw new Error(`Invalid block type: ${block.type}`) } - // Check if this is an agent block with custom tools + // Extract parameters from UI state const params = this.extractParams(block) + + // Validate required fields that only users can provide (before execution starts) + if (validateRequired) { + this.validateRequiredFieldsBeforeExecution(block, blockConfig, params) + } + let toolId = '' if (block.type === 'agent' && params.tools) { @@ -208,6 +216,62 @@ export class Serializer { return params } + private validateRequiredFieldsBeforeExecution( + block: BlockState, + blockConfig: any, + params: Record + ) { + // Validate user-only required fields before execution starts + // This catches missing API keys, credentials, and other user-provided values early + // Fields that are user-or-llm will be validated later after parameter merging + + // Get the tool configuration to check parameter visibility + const toolAccess = blockConfig.tools?.access + if (!toolAccess || toolAccess.length === 0) { + return // No tools to validate against + } + + // Determine the current tool ID using the same logic as the serializer + let currentToolId = '' + try { + currentToolId = blockConfig.tools.config?.tool + ? blockConfig.tools.config.tool(params) + : blockConfig.tools.access[0] + } catch (error) { + logger.warn('Tool selection failed during validation, using default:', { + error: error instanceof Error ? error.message : String(error), + }) + currentToolId = blockConfig.tools.access[0] + } + + // Get the specific tool to validate against + const currentTool = getTool(currentToolId) + if (!currentTool) { + return // Tool not found, skip validation + } + + // Check required user-only parameters for the current tool + const missingFields: string[] = [] + + // Iterate through the tool's parameters, not the block's subBlocks + Object.entries(currentTool.params || {}).forEach(([paramId, paramConfig]) => { + if (paramConfig.required && paramConfig.visibility === 'user-only') { + const fieldValue = params[paramId] + if (fieldValue === undefined || fieldValue === null || fieldValue === '') { + // Find the corresponding subBlock to get the display title + const subBlockConfig = blockConfig.subBlocks?.find((sb: any) => sb.id === paramId) + const displayName = subBlockConfig?.title || paramId + missingFields.push(displayName) + } + } + }) + + if (missingFields.length > 0) { + const blockName = block.name || blockConfig.name || 'Block' + throw new Error(`${blockName} is missing required fields: ${missingFields.join(', ')}`) + } + } + deserializeWorkflow(workflow: SerializedWorkflow): { blocks: Record edges: Edge[] diff --git a/apps/sim/serializer/tests/dual-validation.test.ts b/apps/sim/serializer/tests/dual-validation.test.ts new file mode 100644 index 000000000..a07e2f825 --- /dev/null +++ b/apps/sim/serializer/tests/dual-validation.test.ts @@ -0,0 +1,372 @@ +/** + * @vitest-environment jsdom + * + * Integration Tests for Validation Architecture + * + * These tests verify the complete validation flow: + * 1. Early validation (serialization) - user-only required fields + * 2. Late validation (tool execution) - user-or-llm required fields + */ +import { describe, expect, vi } from 'vitest' +import { Serializer } from '@/serializer/index' +import { validateRequiredParametersAfterMerge } from '@/tools/utils' + +vi.mock('@/blocks', () => ({ + getBlock: (type: string) => { + const mockConfigs: Record = { + jina: { + name: 'Jina', + description: 'Convert website content into text', + category: 'tools', + bgColor: '#333333', + tools: { + access: ['jina_read_url'], + }, + subBlocks: [ + { id: 'url', type: 'short-input', title: 'URL', required: true }, + { id: 'apiKey', type: 'short-input', title: 'API Key', required: true }, + ], + inputs: { + url: { type: 'string' }, + apiKey: { type: 'string' }, + }, + }, + reddit: { + name: 'Reddit', + description: 'Access Reddit data', + category: 'tools', + bgColor: '#FF5700', + tools: { + access: ['reddit_get_posts'], + }, + subBlocks: [ + { id: 'operation', type: 'dropdown', title: 'Operation', required: true }, + { id: 'credential', type: 'oauth-input', title: 'Reddit Account', required: true }, + { id: 'subreddit', type: 'short-input', title: 'Subreddit', required: true }, + ], + inputs: { + operation: { type: 'string' }, + credential: { type: 'string' }, + subreddit: { type: 'string' }, + }, + }, + } + return mockConfigs[type] || null + }, +})) + +vi.mock('@/tools/utils', async () => { + const actual = await vi.importActual('@/tools/utils') + return { + ...actual, + getTool: (toolId: string) => { + const mockTools: Record = { + jina_read_url: { + name: 'Jina Reader', + params: { + url: { + type: 'string', + visibility: 'user-or-llm', + required: true, + description: 'URL to extract content from', + }, + apiKey: { + type: 'string', + visibility: 'user-only', + required: true, + description: 'Your Jina API key', + }, + }, + }, + reddit_get_posts: { + name: 'Reddit Posts', + params: { + subreddit: { + type: 'string', + visibility: 'user-or-llm', + required: true, + description: 'Subreddit name', + }, + credential: { + type: 'string', + visibility: 'user-only', + required: true, + description: 'Reddit credentials', + }, + }, + }, + } + return mockTools[toolId] || null + }, + } +}) + +describe('Validation Integration Tests', () => { + it.concurrent('early validation should catch missing user-only fields', () => { + const serializer = new Serializer() + + // Block missing user-only field (API key) + const blockWithMissingUserOnlyField: any = { + id: 'jina-block', + type: 'jina', + name: 'Jina Content Extractor', + position: { x: 0, y: 0 }, + subBlocks: { + url: { value: 'https://example.com' }, // Present + apiKey: { value: null }, // Missing user-only field + }, + outputs: {}, + enabled: true, + } + + // Should fail at serialization (early validation) + expect(() => { + serializer.serializeWorkflow( + { 'jina-block': blockWithMissingUserOnlyField }, + [], + {}, + undefined, + true + ) + }).toThrow('Jina Content Extractor is missing required fields: API Key') + }) + + it.concurrent( + 'early validation should allow missing user-or-llm fields (LLM can provide later)', + () => { + const serializer = new Serializer() + + // Block missing user-or-llm field (URL) but has user-only field (API key) + const blockWithMissingUserOrLlmField: any = { + id: 'jina-block', + type: 'jina', + name: 'Jina Content Extractor', + position: { x: 0, y: 0 }, + subBlocks: { + url: { value: null }, // Missing user-or-llm field (LLM can provide) + apiKey: { value: 'test-api-key' }, // Present user-only field + }, + outputs: {}, + enabled: true, + } + + // Should pass serialization (early validation doesn't check user-or-llm fields) + expect(() => { + serializer.serializeWorkflow( + { 'jina-block': blockWithMissingUserOrLlmField }, + [], + {}, + undefined, + true + ) + }).not.toThrow() + } + ) + + it.concurrent( + 'late validation should catch missing user-or-llm fields after parameter merge', + () => { + // Simulate parameters after user + LLM merge + const mergedParams = { + url: null, // Missing user-or-llm field + apiKey: 'test-api-key', // Present user-only field + } + + // Should fail at tool validation (late validation) + expect(() => { + validateRequiredParametersAfterMerge( + 'jina_read_url', + { + name: 'Jina Reader', + params: { + url: { + type: 'string', + visibility: 'user-or-llm', + required: true, + description: 'URL to extract content from', + }, + apiKey: { + type: 'string', + visibility: 'user-only', + required: true, + description: 'Your Jina API key', + }, + }, + } as any, + mergedParams + ) + }).toThrow('"Url" is required for Jina Reader') + } + ) + + it.concurrent('late validation should NOT validate user-only fields (validated earlier)', () => { + // Simulate parameters after user + LLM merge - missing user-only field + const mergedParams = { + url: 'https://example.com', // Present user-or-llm field + apiKey: null, // Missing user-only field (but shouldn't be checked here) + } + + // Should pass tool validation (late validation doesn't check user-only fields) + expect(() => { + validateRequiredParametersAfterMerge( + 'jina_read_url', + { + name: 'Jina Reader', + params: { + url: { + type: 'string', + visibility: 'user-or-llm', + required: true, + description: 'URL to extract content from', + }, + apiKey: { + type: 'string', + visibility: 'user-only', + required: true, + description: 'Your Jina API key', + }, + }, + } as any, + mergedParams + ) + }).not.toThrow() + }) + + it.concurrent('complete validation flow: both layers working together', () => { + const serializer = new Serializer() + + // Scenario 1: Missing user-only field - should fail at serialization + const blockMissingUserOnly: any = { + id: 'reddit-block', + type: 'reddit', + name: 'Reddit Posts', + position: { x: 0, y: 0 }, + subBlocks: { + operation: { value: 'get_posts' }, + credential: { value: null }, // Missing user-only + subreddit: { value: 'programming' }, // Present user-or-llm + }, + outputs: {}, + enabled: true, + } + + expect(() => { + serializer.serializeWorkflow( + { 'reddit-block': blockMissingUserOnly }, + [], + {}, + undefined, + true + ) + }).toThrow('Reddit Posts is missing required fields: Reddit Account') + + // Scenario 2: Has user-only fields but missing user-or-llm - should pass serialization + const blockMissingUserOrLlm: any = { + id: 'reddit-block', + type: 'reddit', + name: 'Reddit Posts', + position: { x: 0, y: 0 }, + subBlocks: { + operation: { value: 'get_posts' }, + credential: { value: 'reddit-token' }, // Present user-only + subreddit: { value: null }, // Missing user-or-llm + }, + outputs: {}, + enabled: true, + } + + // Should pass serialization + expect(() => { + serializer.serializeWorkflow( + { 'reddit-block': blockMissingUserOrLlm }, + [], + {}, + undefined, + true + ) + }).not.toThrow() + + // But should fail at tool validation + const mergedParams = { + subreddit: null, // Missing user-or-llm field + credential: 'reddit-token', // Present user-only field + } + + expect(() => { + validateRequiredParametersAfterMerge( + 'reddit_get_posts', + { + name: 'Reddit Posts', + params: { + subreddit: { + type: 'string', + visibility: 'user-or-llm', + required: true, + description: 'Subreddit name', + }, + credential: { + type: 'string', + visibility: 'user-only', + required: true, + description: 'Reddit credentials', + }, + }, + } as any, + mergedParams + ) + }).toThrow('"Subreddit" is required for Reddit Posts') + }) + + it.concurrent('complete success: all required fields provided correctly', () => { + const serializer = new Serializer() + + // Block with all required fields present + const completeBlock: any = { + id: 'jina-block', + type: 'jina', + name: 'Jina Content Extractor', + position: { x: 0, y: 0 }, + subBlocks: { + url: { value: 'https://example.com' }, // Present user-or-llm + apiKey: { value: 'test-api-key' }, // Present user-only + }, + outputs: {}, + enabled: true, + } + + // Should pass serialization (early validation) + expect(() => { + serializer.serializeWorkflow({ 'jina-block': completeBlock }, [], {}, undefined, true) + }).not.toThrow() + + // Should pass tool validation (late validation) + const completeParams = { + url: 'https://example.com', + apiKey: 'test-api-key', + } + + expect(() => { + validateRequiredParametersAfterMerge( + 'jina_read_url', + { + name: 'Jina Reader', + params: { + url: { + type: 'string', + visibility: 'user-or-llm', + required: true, + description: 'URL to extract content from', + }, + apiKey: { + type: 'string', + visibility: 'user-only', + required: true, + description: 'Your Jina API key', + }, + }, + } as any, + completeParams + ) + }).not.toThrow() + }) +}) diff --git a/apps/sim/stores/workflows/workflow/utils.test.ts b/apps/sim/stores/workflows/workflow/utils.test.ts index e2ed70db0..eaf3cedcc 100644 --- a/apps/sim/stores/workflows/workflow/utils.test.ts +++ b/apps/sim/stores/workflows/workflow/utils.test.ts @@ -1,9 +1,9 @@ -import { describe, expect, test } from 'vitest' +import { describe, expect } from 'vitest' import type { BlockState } from '@/stores/workflows/workflow/types' import { convertLoopBlockToLoop } from '@/stores/workflows/workflow/utils' describe('convertLoopBlockToLoop', () => { - test('should parse JSON array string for forEach loops', () => { + it.concurrent('should parse JSON array string for forEach loops', () => { const blocks: Record = { loop1: { id: 'loop1', @@ -29,7 +29,7 @@ describe('convertLoopBlockToLoop', () => { expect(result?.iterations).toBe(10) }) - test('should parse JSON object string for forEach loops', () => { + it.concurrent('should parse JSON object string for forEach loops', () => { const blocks: Record = { loop1: { id: 'loop1', @@ -54,7 +54,7 @@ describe('convertLoopBlockToLoop', () => { expect(result?.forEachItems).toEqual({ key1: 'value1', key2: 'value2' }) }) - test('should keep string as-is if not valid JSON', () => { + it.concurrent('should keep string as-is if not valid JSON', () => { const blocks: Record = { loop1: { id: 'loop1', @@ -78,7 +78,7 @@ describe('convertLoopBlockToLoop', () => { expect(result?.forEachItems).toBe('') }) - test('should handle empty collection', () => { + it.concurrent('should handle empty collection', () => { const blocks: Record = { loop1: { id: 'loop1', @@ -102,7 +102,7 @@ describe('convertLoopBlockToLoop', () => { expect(result?.forEachItems).toBe('') }) - test('should handle for loops without collection parsing', () => { + it.concurrent('should handle for loops without collection parsing', () => { const blocks: Record = { loop1: { id: 'loop1', diff --git a/apps/sim/tools/arxiv/get_paper.ts b/apps/sim/tools/arxiv/get_paper.ts index d37d331d4..abe211250 100644 --- a/apps/sim/tools/arxiv/get_paper.ts +++ b/apps/sim/tools/arxiv/get_paper.ts @@ -13,7 +13,7 @@ export const getPaperTool: ToolConfig = { version: '1.0.0', params: { - query: { + searchQuery: { type: 'string', required: true, visibility: 'user-or-llm', @@ -48,9 +48,9 @@ export const searchTool: ToolConfig = { const searchParams = new URLSearchParams() // Build search query - let searchQuery = params.query + let searchQuery = params.searchQuery if (params.searchField && params.searchField !== 'all') { - searchQuery = `${params.searchField}:${params.query}` + searchQuery = `${params.searchField}:${params.searchQuery}` } searchParams.append('search_query', searchQuery) diff --git a/apps/sim/tools/arxiv/types.ts b/apps/sim/tools/arxiv/types.ts index 6cdc366ac..7cf8062b4 100644 --- a/apps/sim/tools/arxiv/types.ts +++ b/apps/sim/tools/arxiv/types.ts @@ -3,7 +3,7 @@ import type { ToolResponse } from '@/tools/types' // Search tool types export interface ArxivSearchParams { - query: string + searchQuery: string searchField?: 'all' | 'ti' | 'au' | 'abs' | 'co' | 'jr' | 'cat' | 'rn' maxResults?: number sortBy?: 'relevance' | 'lastUpdatedDate' | 'submittedDate' diff --git a/apps/sim/tools/exa/search.ts b/apps/sim/tools/exa/search.ts index 76fa3f5e1..a27b00148 100644 --- a/apps/sim/tools/exa/search.ts +++ b/apps/sim/tools/exa/search.ts @@ -31,7 +31,7 @@ export const searchTool: ToolConfig = { type: 'string', required: false, visibility: 'user-only', - description: 'Search type: neural, keyword, auto or magic (default: auto)', + description: 'Search type: neural, keyword, auto or fast (default: auto)', }, apiKey: { type: 'string', diff --git a/apps/sim/tools/exa/types.ts b/apps/sim/tools/exa/types.ts index 09106457e..bc4bd6420 100644 --- a/apps/sim/tools/exa/types.ts +++ b/apps/sim/tools/exa/types.ts @@ -11,7 +11,7 @@ export interface ExaSearchParams extends ExaBaseParams { query: string numResults?: number useAutoprompt?: boolean - type?: 'auto' | 'neural' | 'keyword' | 'magic' + type?: 'auto' | 'neural' | 'keyword' | 'fast' } export interface ExaSearchResult { diff --git a/apps/sim/tools/function/execute.test.ts b/apps/sim/tools/function/execute.test.ts index b8b31b2dc..bf5713540 100644 --- a/apps/sim/tools/function/execute.test.ts +++ b/apps/sim/tools/function/execute.test.ts @@ -6,7 +6,7 @@ * This file contains unit tests for the Function Execute tool, * which runs JavaScript code in a secure sandbox. */ -import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' +import { afterEach, beforeEach, describe, expect, vi } from 'vitest' import { ToolTester } from '@/tools/__test-utils__/test-tools' import { functionExecuteTool } from '@/tools/function/execute' @@ -25,12 +25,12 @@ describe('Function Execute Tool', () => { }) describe('Request Construction', () => { - test('should set correct URL for code execution', () => { + it.concurrent('should set correct URL for code execution', () => { // Since this is an internal route, actual URL will be the concatenated base URL + path expect(tester.getRequestUrl({})).toBe('/api/function/execute') }) - test('should include correct headers for JSON payload', () => { + it.concurrent('should include correct headers for JSON payload', () => { const headers = tester.getRequestHeaders({ code: 'return 42', }) @@ -38,7 +38,7 @@ describe('Function Execute Tool', () => { expect(headers['Content-Type']).toBe('application/json') }) - test('should format single string code correctly', () => { + it.concurrent('should format single string code correctly', () => { const body = tester.getRequestBody({ code: 'return 42', envVars: {}, @@ -58,7 +58,7 @@ describe('Function Execute Tool', () => { }) }) - test('should format array of code blocks correctly', () => { + it.concurrent('should format array of code blocks correctly', () => { const body = tester.getRequestBody({ code: [ { content: 'const x = 40;', id: 'block1' }, @@ -82,7 +82,7 @@ describe('Function Execute Tool', () => { }) }) - test('should use default timeout and memory limit when not provided', () => { + it.concurrent('should use default timeout and memory limit when not provided', () => { const body = tester.getRequestBody({ code: 'return 42', }) @@ -100,7 +100,7 @@ describe('Function Execute Tool', () => { }) describe('Response Handling', () => { - test('should process successful code execution response', async () => { + it.concurrent('should process successful code execution response', async () => { // Setup a successful response tester.setup({ success: true, @@ -121,7 +121,7 @@ describe('Function Execute Tool', () => { expect(result.output.stdout).toBe('console.log output') }) - test('should handle execution errors', async () => { + it.concurrent('should handle execution errors', async () => { // Setup error response tester.setup( { @@ -142,7 +142,7 @@ describe('Function Execute Tool', () => { expect(result.error).toBe('Syntax error in code') }) - test('should handle timeout errors', async () => { + it.concurrent('should handle timeout errors', async () => { // Setup timeout error response tester.setup( { @@ -165,7 +165,7 @@ describe('Function Execute Tool', () => { }) describe('Error Handling', () => { - test('should handle syntax error with line content', async () => { + it.concurrent('should handle syntax error with line content', async () => { // Setup error response with debug information tester.setup( { @@ -202,7 +202,7 @@ describe('Function Execute Tool', () => { expect(result.error).toContain('(Check for missing quotes, brackets, or semicolons)') }) - test('should handle runtime error with line and column', async () => { + it.concurrent('should handle runtime error with line and column', async () => { // Setup runtime error response tester.setup( { @@ -238,7 +238,7 @@ describe('Function Execute Tool', () => { expect(result.error).toContain('Cannot read properties of null') }) - test('should handle error information in tool response', async () => { + it.concurrent('should handle error information in tool response', async () => { // Setup error response with full debug info tester.setup( { @@ -272,7 +272,7 @@ describe('Function Execute Tool', () => { ) }) - test('should preserve debug information in error object', async () => { + it.concurrent('should preserve debug information in error object', async () => { // Setup error response tester.setup( { @@ -301,7 +301,7 @@ describe('Function Execute Tool', () => { // Note: In this test framework, debug information would be available in the response object, but the tool transforms it into the error message }) - test('should handle enhanced error without line information', async () => { + it.concurrent('should handle enhanced error without line information', async () => { // Setup error response without line information tester.setup( { @@ -325,7 +325,7 @@ describe('Function Execute Tool', () => { expect(result.error).toBe('Generic error message') }) - test('should provide line-specific error message when available', async () => { + it.concurrent('should provide line-specific error message when available', async () => { // Setup error response with line info tester.setup( { @@ -355,7 +355,7 @@ describe('Function Execute Tool', () => { }) describe('Edge Cases', () => { - test('should handle empty code input', async () => { + it.concurrent('should handle empty code input', async () => { // Execute with empty code - this should still pass through to the API await tester.execute({ code: '', @@ -366,7 +366,7 @@ describe('Function Execute Tool', () => { expect(body.code).toBe('') }) - test('should handle extremely short timeout', async () => { + it.concurrent('should handle extremely short timeout', async () => { // Edge case with very short timeout const body = tester.getRequestBody({ code: 'return 42', diff --git a/apps/sim/tools/http/request.test.ts b/apps/sim/tools/http/request.test.ts index 814f7de22..09ad477da 100644 --- a/apps/sim/tools/http/request.test.ts +++ b/apps/sim/tools/http/request.test.ts @@ -6,7 +6,7 @@ * This file contains unit tests for the HTTP Request tool, which is used * to make HTTP requests to external APIs and services. */ -import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' +import { afterEach, beforeEach, describe, expect, vi } from 'vitest' import { mockHttpResponses } from '@/tools/__test-utils__/mock-data' import { ToolTester } from '@/tools/__test-utils__/test-tools' import { requestTool } from '@/tools/http/request' @@ -29,7 +29,7 @@ describe('HTTP Request Tool', () => { }) describe('URL Construction', () => { - test('should construct URLs correctly', () => { + it.concurrent('should construct URLs correctly', () => { // Base URL expect(tester.getRequestUrl({ url: 'https://api.example.com/data' })).toBe( 'https://api.example.com/data' @@ -76,7 +76,7 @@ describe('HTTP Request Tool', () => { }) describe('Headers Construction', () => { - test('should set headers correctly', () => { + it.concurrent('should set headers correctly', () => { // Default headers expect(tester.getRequestHeaders({ url: 'https://api.example.com', method: 'GET' })).toEqual( {} @@ -109,7 +109,7 @@ describe('HTTP Request Tool', () => { }) }) - test('should set dynamic Referer header correctly', async () => { + it('should set dynamic Referer header correctly', async () => { const originalWindow = global.window Object.defineProperty(global, 'window', { value: { @@ -137,7 +137,7 @@ describe('HTTP Request Tool', () => { global.window = originalWindow }) - test('should set dynamic Host header correctly', async () => { + it('should set dynamic Host header correctly', async () => { // Setup mock response tester.setup(mockHttpResponses.simple) @@ -165,7 +165,7 @@ describe('HTTP Request Tool', () => { }) describe('Request Execution', () => { - test('should apply default and dynamic headers to requests', async () => { + it('should apply default and dynamic headers to requests', async () => { // Setup mock response tester.setup(mockHttpResponses.simple) @@ -204,7 +204,7 @@ describe('HTTP Request Tool', () => { global.window = originalWindow }) - test('should handle successful GET requests', async () => { + it('should handle successful GET requests', async () => { // Setup mock response tester.setup(mockHttpResponses.simple) @@ -221,7 +221,7 @@ describe('HTTP Request Tool', () => { expect(result.output.headers).toHaveProperty('content-type') }) - test('should handle POST requests with body', async () => { + it('should handle POST requests with body', async () => { // Setup mock response tester.setup({ result: 'success' }) @@ -253,7 +253,7 @@ describe('HTTP Request Tool', () => { expect(bodyArg).toEqual(body) }) - test('should handle errors correctly', async () => { + it('should handle errors correctly', async () => { // Setup error response tester.setup(mockHttpResponses.error, { ok: false, status: 400 }) @@ -268,7 +268,7 @@ describe('HTTP Request Tool', () => { expect(result.error).toBeDefined() }) - test('should handle timeout parameter', async () => { + it('should handle timeout parameter', async () => { // Setup successful response tester.setup({ result: 'success' }) @@ -285,7 +285,7 @@ describe('HTTP Request Tool', () => { }) describe('Response Transformation', () => { - test('should transform JSON responses correctly', async () => { + it('should transform JSON responses correctly', async () => { // Setup JSON response tester.setup({ data: { key: 'value' } }, { headers: { 'content-type': 'application/json' } }) @@ -299,7 +299,7 @@ describe('HTTP Request Tool', () => { expect(result.output.data).toEqual({ data: { key: 'value' } }) }) - test('should transform text responses correctly', async () => { + it('should transform text responses correctly', async () => { // Setup text response const textContent = 'Plain text response' tester.setup(textContent, { headers: { 'content-type': 'text/plain' } }) @@ -316,7 +316,7 @@ describe('HTTP Request Tool', () => { }) describe('Error Handling', () => { - test('should handle network errors', async () => { + it('should handle network errors', async () => { // Setup network error tester.setupError('Network error') @@ -330,7 +330,7 @@ describe('HTTP Request Tool', () => { expect(result.error).toContain('Network error') }) - test('should handle 404 errors', async () => { + it('should handle 404 errors', async () => { // Setup 404 response tester.setup(mockHttpResponses.notFound, { ok: false, status: 404 }) @@ -344,7 +344,7 @@ describe('HTTP Request Tool', () => { expect(result.output).toEqual({}) }) - test('should handle 401 unauthorized errors', async () => { + it('should handle 401 unauthorized errors', async () => { // Setup 401 response tester.setup(mockHttpResponses.unauthorized, { ok: false, status: 401 }) @@ -360,7 +360,7 @@ describe('HTTP Request Tool', () => { }) describe('Default Headers', () => { - test('should apply all default headers correctly', async () => { + it('should apply all default headers correctly', async () => { // Setup mock response tester.setup(mockHttpResponses.simple) @@ -401,7 +401,7 @@ describe('HTTP Request Tool', () => { global.window = originalWindow }) - test('should allow overriding default headers', async () => { + it('should allow overriding default headers', async () => { // Setup mock response tester.setup(mockHttpResponses.simple) @@ -430,7 +430,7 @@ describe('HTTP Request Tool', () => { }) describe('Proxy Functionality', () => { - test('should not use proxy in test environment', () => { + it.concurrent('should not use proxy in test environment', () => { // This test verifies that the shouldUseProxy function has been disabled for tests // Create a browser-like environment @@ -453,7 +453,7 @@ describe('HTTP Request Tool', () => { global.window = originalWindow }) - test('should include method parameter in proxy URL', () => { + it.concurrent('should include method parameter in proxy URL', () => { const originalWindow = global.window Object.defineProperty(global, 'window', { value: { diff --git a/apps/sim/tools/hunter/companies_find.ts b/apps/sim/tools/hunter/companies_find.ts new file mode 100644 index 000000000..e25c54e12 --- /dev/null +++ b/apps/sim/tools/hunter/companies_find.ts @@ -0,0 +1,78 @@ +import type { HunterEnrichmentParams, HunterEnrichmentResponse } from '@/tools/hunter/types' +import type { ToolConfig } from '@/tools/types' + +export const companiesFindTool: ToolConfig = { + id: 'hunter_companies_find', + name: 'Hunter Companies Find', + description: 'Enriches company data using domain name.', + version: '1.0.0', + + params: { + domain: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Domain to find company data for', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Hunter.io API Key', + }, + }, + + request: { + url: (params) => { + const url = new URL('https://api.hunter.io/v2/companies/find') + url.searchParams.append('api_key', params.apiKey) + url.searchParams.append('domain', params.domain || '') + + return url.toString() + }, + method: 'GET', + isInternalRoute: false, + headers: () => ({ + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + // Extract specific error message from Hunter.io API + const errorMessage = + data.errors?.[0]?.details || + data.message || + `HTTP ${response.status}: Failed to find company data` + throw new Error(errorMessage) + } + + return { + success: true, + output: { + person: undefined, + company: data.data + ? { + name: data.data.name || '', + domain: data.data.domain || '', + industry: data.data.industry || '', + size: data.data.size || '', + country: data.data.country || '', + linkedin: data.data.linkedin || '', + twitter: data.data.twitter || '', + } + : undefined, + }, + } + }, + + transformError: (error) => { + if (error instanceof Error) { + // Return the exact error message from the API + return error.message + } + return 'An unexpected error occurred while finding company data' + }, +} diff --git a/apps/sim/tools/hunter/discover.ts b/apps/sim/tools/hunter/discover.ts new file mode 100644 index 000000000..0c037d1de --- /dev/null +++ b/apps/sim/tools/hunter/discover.ts @@ -0,0 +1,120 @@ +import type { HunterDiscoverParams, HunterDiscoverResponse } from '@/tools/hunter/types' +import type { ToolConfig } from '@/tools/types' + +export const discoverTool: ToolConfig = { + id: 'hunter_discover', + name: 'Hunter Discover', + description: 'Returns companies matching a set of criteria using Hunter.io AI-powered search.', + version: '1.0.0', + + params: { + query: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Natural language search query for companies', + }, + domain: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Company domain names to filter by', + }, + headcount: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Company size filter (e.g., "1-10", "11-50")', + }, + company_type: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Type of organization', + }, + technology: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Technology used by companies', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Hunter.io API Key', + }, + }, + + request: { + url: (params) => { + // Validate that at least one search parameter is provided + if ( + !params.query && + !params.domain && + !params.headcount && + !params.company_type && + !params.technology + ) { + throw new Error( + 'At least one search parameter (query, domain, headcount, company_type, or technology) must be provided' + ) + } + + const url = new URL('https://api.hunter.io/v2/discover') + url.searchParams.append('api_key', params.apiKey) + return url.toString() + }, + method: 'POST', + isInternalRoute: false, + headers: () => ({ + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: Record = {} + + // Add optional parameters if provided + if (params.query) body.query = params.query + if (params.domain) body.organization = { domain: [params.domain] } + if (params.headcount) body.headcount = params.headcount + if (params.company_type) body.company_type = params.company_type + if (params.technology) { + body.technology = { + include: [params.technology], + } + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error( + data.errors?.[0]?.details || data.message || 'Failed to perform Hunter discover' + ) + } + + return { + success: true, + output: { + results: + data.data?.map((company: any) => ({ + domain: company.domain || '', + name: company.organization || '', + headcount: company.headcount, + technologies: company.technologies || [], + email_count: company.emails_count?.total || 0, + })) || [], + }, + } + }, + + transformError: (error) => { + return error instanceof Error + ? error.message + : 'An error occurred while performing the Hunter discover' + }, +} diff --git a/apps/sim/tools/hunter/domain_search.ts b/apps/sim/tools/hunter/domain_search.ts new file mode 100644 index 000000000..79eec15ea --- /dev/null +++ b/apps/sim/tools/hunter/domain_search.ts @@ -0,0 +1,133 @@ +import type { HunterDomainSearchParams, HunterDomainSearchResponse } from '@/tools/hunter/types' +import type { ToolConfig } from '@/tools/types' + +export const domainSearchTool: ToolConfig = { + id: 'hunter_domain_search', + name: 'Hunter Domain Search', + description: 'Returns all the email addresses found using one given domain name, with sources.', + version: '1.0.0', + + params: { + domain: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Domain name to search for email addresses', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'Maximum email addresses to return (default: 10)', + }, + offset: { + type: 'number', + required: false, + visibility: 'hidden', + description: 'Number of email addresses to skip', + }, + type: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Filter for personal or generic emails', + }, + seniority: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Filter by seniority level: junior, senior, or executive', + }, + department: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by specific departments (e.g., sales, marketing)', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Hunter.io API Key', + }, + }, + + request: { + url: (params) => { + const url = new URL('https://api.hunter.io/v2/domain-search') + url.searchParams.append('domain', params.domain) + url.searchParams.append('api_key', params.apiKey) + + if (params.limit) url.searchParams.append('limit', params.limit.toString()) + if (params.offset) url.searchParams.append('offset', params.offset.toString()) + if (params.type && params.type !== 'all') url.searchParams.append('type', params.type) + if (params.seniority && params.seniority !== 'all') + url.searchParams.append('seniority', params.seniority) + if (params.department) url.searchParams.append('department', params.department) + + return url.toString() + }, + method: 'GET', + isInternalRoute: false, + headers: () => ({ + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error( + data.errors?.[0]?.details || data.message || 'Failed to perform Hunter domain search' + ) + } + + return { + success: true, + output: { + domain: data.data?.domain || '', + disposable: data.data?.disposable || false, + webmail: data.data?.webmail || false, + accept_all: data.data?.accept_all || false, + pattern: data.data?.pattern || '', + organization: data.data?.organization || '', + description: data.data?.description || '', + industry: data.data?.industry || '', + twitter: data.data?.twitter || '', + facebook: data.data?.facebook || '', + linkedin: data.data?.linkedin || '', + instagram: data.data?.instagram || '', + youtube: data.data?.youtube || '', + technologies: data.data?.technologies || [], + country: data.data?.country || '', + state: data.data?.state || '', + city: data.data?.city || '', + postal_code: data.data?.postal_code || '', + street: data.data?.street || '', + emails: + data.data?.emails?.map((email: any) => ({ + value: email.value || '', + type: email.type || '', + confidence: email.confidence || 0, + sources: email.sources || [], + first_name: email.first_name || '', + last_name: email.last_name || '', + position: email.position || '', + seniority: email.seniority || '', + department: email.department || '', + linkedin: email.linkedin || '', + twitter: email.twitter || '', + phone_number: email.phone_number || '', + verification: email.verification || {}, + })) || [], + }, + } + }, + + transformError: (error) => { + return error instanceof Error + ? error.message + : 'An error occurred while performing the Hunter domain search' + }, +} diff --git a/apps/sim/tools/hunter/email_count.ts b/apps/sim/tools/hunter/email_count.ts new file mode 100644 index 000000000..6658cbd67 --- /dev/null +++ b/apps/sim/tools/hunter/email_count.ts @@ -0,0 +1,100 @@ +import type { HunterEmailCountParams, HunterEmailCountResponse } from '@/tools/hunter/types' +import type { ToolConfig } from '@/tools/types' + +export const emailCountTool: ToolConfig = { + id: 'hunter_email_count', + name: 'Hunter Email Count', + description: 'Returns the total number of email addresses found for a domain or company.', + version: '1.0.0', + + params: { + domain: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Domain to count emails for (required if company not provided)', + }, + company: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Company name to count emails for (required if domain not provided)', + }, + type: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Filter for personal or generic emails only', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Hunter.io API Key', + }, + }, + + request: { + url: (params) => { + if (!params.domain && !params.company) { + throw new Error('Either domain or company must be provided') + } + + const url = new URL('https://api.hunter.io/v2/email-count') + url.searchParams.append('api_key', params.apiKey) + + if (params.domain) url.searchParams.append('domain', params.domain) + if (params.company) url.searchParams.append('company', params.company) + if (params.type && params.type !== 'all') url.searchParams.append('type', params.type) + + return url.toString() + }, + method: 'GET', + isInternalRoute: false, + headers: () => ({ + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error( + data.errors?.[0]?.details || data.message || 'Failed to perform Hunter email count' + ) + } + + return { + success: true, + output: { + total: data.data?.total || 0, + personal_emails: data.data?.personal_emails || 0, + generic_emails: data.data?.generic_emails || 0, + department: data.data?.department || { + executive: 0, + it: 0, + finance: 0, + management: 0, + sales: 0, + legal: 0, + support: 0, + hr: 0, + marketing: 0, + communication: 0, + }, + seniority: data.data?.seniority || { + junior: 0, + senior: 0, + executive: 0, + }, + }, + } + }, + + transformError: (error) => { + return error instanceof Error + ? error.message + : 'An error occurred while performing the Hunter email count' + }, +} diff --git a/apps/sim/tools/hunter/email_finder.ts b/apps/sim/tools/hunter/email_finder.ts new file mode 100644 index 000000000..2ec48dfdb --- /dev/null +++ b/apps/sim/tools/hunter/email_finder.ts @@ -0,0 +1,88 @@ +import type { HunterEmailFinderParams, HunterEmailFinderResponse } from '@/tools/hunter/types' +import type { ToolConfig } from '@/tools/types' + +export const emailFinderTool: ToolConfig = { + id: 'hunter_email_finder', + name: 'Hunter Email Finder', + description: + 'Finds the most likely email address for a person given their name and company domain.', + version: '1.0.0', + + params: { + domain: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Company domain name', + }, + first_name: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: "Person's first name", + }, + last_name: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: "Person's last name", + }, + company: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Company name', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Hunter.io API Key', + }, + }, + + request: { + url: (params) => { + const url = new URL('https://api.hunter.io/v2/email-finder') + url.searchParams.append('domain', params.domain) + url.searchParams.append('first_name', params.first_name) + url.searchParams.append('last_name', params.last_name) + url.searchParams.append('api_key', params.apiKey) + + if (params.company) url.searchParams.append('company', params.company) + + return url.toString() + }, + method: 'GET', + isInternalRoute: false, + headers: () => ({ + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error( + data.errors?.[0]?.details || data.message || 'Failed to perform Hunter email finder' + ) + } + + return { + success: true, + output: { + email: data.data?.email || '', + score: data.data?.score || 0, + sources: data.data?.sources || [], + verification: data.data?.verification || {}, + }, + } + }, + + transformError: (error) => { + return error instanceof Error + ? error.message + : 'An error occurred while performing the Hunter email finder' + }, +} diff --git a/apps/sim/tools/hunter/email_verifier.ts b/apps/sim/tools/hunter/email_verifier.ts new file mode 100644 index 000000000..3ff449cfd --- /dev/null +++ b/apps/sim/tools/hunter/email_verifier.ts @@ -0,0 +1,82 @@ +import type { HunterEmailVerifierParams, HunterEmailVerifierResponse } from '@/tools/hunter/types' +import type { ToolConfig } from '@/tools/types' + +export const emailVerifierTool: ToolConfig = + { + id: 'hunter_email_verifier', + name: 'Hunter Email Verifier', + description: + 'Verifies the deliverability of an email address and provides detailed verification status.', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The email address to verify', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Hunter.io API Key', + }, + }, + + request: { + url: (params) => { + const url = new URL('https://api.hunter.io/v2/email-verifier') + url.searchParams.append('email', params.email) + url.searchParams.append('api_key', params.apiKey) + + return url.toString() + }, + method: 'GET', + isInternalRoute: false, + headers: () => ({ + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + // Extract specific error message from Hunter.io API + const errorMessage = + data.errors?.[0]?.details || + data.message || + `HTTP ${response.status}: Failed to verify email` + throw new Error(errorMessage) + } + + return { + success: true, + output: { + result: data.data?.result || 'unknown', + score: data.data?.score || 0, + email: data.data?.email || '', + regexp: data.data?.regexp || false, + gibberish: data.data?.gibberish || false, + disposable: data.data?.disposable || false, + webmail: data.data?.webmail || false, + mx_records: data.data?.mx_records || false, + smtp_server: data.data?.smtp_server || false, + smtp_check: data.data?.smtp_check || false, + accept_all: data.data?.accept_all || false, + block: data.data?.block || false, + status: data.data?.status || 'unknown', + sources: data.data?.sources || [], + }, + } + }, + + transformError: (error) => { + if (error instanceof Error) { + // Return the exact error message from the API + return error.message + } + return 'An unexpected error occurred while verifying email' + }, + } diff --git a/apps/sim/tools/hunter/index.ts b/apps/sim/tools/hunter/index.ts new file mode 100644 index 000000000..6364f5b59 --- /dev/null +++ b/apps/sim/tools/hunter/index.ts @@ -0,0 +1,13 @@ +import { companiesFindTool } from '@/tools/hunter/companies_find' +import { discoverTool } from '@/tools/hunter/discover' +import { domainSearchTool } from '@/tools/hunter/domain_search' +import { emailCountTool } from '@/tools/hunter/email_count' +import { emailFinderTool } from '@/tools/hunter/email_finder' +import { emailVerifierTool } from '@/tools/hunter/email_verifier' + +export const hunterDiscoverTool = discoverTool +export const hunterDomainSearchTool = domainSearchTool +export const hunterEmailFinderTool = emailFinderTool +export const hunterEmailVerifierTool = emailVerifierTool +export const hunterCompaniesFindTool = companiesFindTool +export const hunterEmailCountTool = emailCountTool diff --git a/apps/sim/tools/hunter/types.ts b/apps/sim/tools/hunter/types.ts new file mode 100644 index 000000000..2c2af0082 --- /dev/null +++ b/apps/sim/tools/hunter/types.ts @@ -0,0 +1,218 @@ +// Common types for Hunter.io tools +import type { ToolResponse } from '@/tools/types' + +// Common parameters for all Hunter.io tools +export interface HunterBaseParams { + apiKey: string +} + +// Discover tool types +export interface HunterDiscoverParams extends HunterBaseParams { + query?: string + domain?: string + headcount?: string + company_type?: string + technology?: string +} + +export interface HunterDiscoverResult { + domain: string + name: string + headcount?: number + technologies?: string[] + email_count?: number +} + +export interface HunterDiscoverResponse extends ToolResponse { + output: { + results: HunterDiscoverResult[] + } +} + +// Domain Search tool types +export interface HunterDomainSearchParams extends HunterBaseParams { + domain: string + limit?: number + offset?: number + type?: 'personal' | 'generic' | 'all' + seniority?: 'junior' | 'senior' | 'executive' | 'all' + department?: string +} + +export interface HunterEmail { + value: string + type: string + confidence: number + sources: Array<{ + domain: string + uri: string + extracted_on: string + last_seen_on: string + still_on_page: boolean + }> + first_name: string + last_name: string + position: string + seniority: string + department: string + linkedin: string + twitter: string + phone_number: string + verification: { + date: string + status: string + } +} + +export interface HunterDomainSearchResponse extends ToolResponse { + output: { + domain: string + disposable: boolean + webmail: boolean + accept_all: boolean + pattern: string + organization: string + description: string + industry: string + twitter: string + facebook: string + linkedin: string + instagram: string + youtube: string + technologies: string[] + country: string + state: string + city: string + postal_code: string + street: string + emails: HunterEmail[] + } +} + +// Email Finder tool types +export interface HunterEmailFinderParams extends HunterBaseParams { + domain: string + first_name: string + last_name: string + company?: string +} + +export interface HunterEmailFinderResponse extends ToolResponse { + output: { + email: string + score: number + sources: Array<{ + domain: string + uri: string + extracted_on: string + last_seen_on: string + still_on_page: boolean + }> + verification: { + date: string + status: string + } + } +} + +// Email Verifier tool types +export interface HunterEmailVerifierParams extends HunterBaseParams { + email: string +} + +export interface HunterEmailVerifierResponse extends ToolResponse { + output: { + result: 'deliverable' | 'undeliverable' | 'risky' + score: number + email: string + regexp: boolean + gibberish: boolean + disposable: boolean + webmail: boolean + mx_records: boolean + smtp_server: boolean + smtp_check: boolean + accept_all: boolean + block: boolean + status: 'valid' | 'invalid' | 'accept_all' | 'webmail' | 'disposable' | 'unknown' + sources: Array<{ + domain: string + uri: string + extracted_on: string + last_seen_on: string + still_on_page: boolean + }> + } +} + +// Enrichment tool types +export interface HunterEnrichmentParams extends HunterBaseParams { + email?: string + domain?: string + linkedin_handle?: string +} + +export interface HunterEnrichmentResponse extends ToolResponse { + output: { + person?: { + first_name: string + last_name: string + email: string + position: string + seniority: string + department: string + linkedin: string + twitter: string + phone_number: string + } + company?: { + name: string + domain: string + industry: string + size: string + country: string + linkedin: string + twitter: string + } + } +} + +// Email Count tool types +export interface HunterEmailCountParams extends HunterBaseParams { + domain?: string + company?: string + type?: 'personal' | 'generic' | 'all' +} + +export interface HunterEmailCountResponse extends ToolResponse { + output: { + total: number + personal_emails: number + generic_emails: number + department: { + executive: number + it: number + finance: number + management: number + sales: number + legal: number + support: number + hr: number + marketing: number + communication: number + } + seniority: { + junior: number + senior: number + executive: number + } + } +} + +export type HunterResponse = + | HunterDiscoverResponse + | HunterDomainSearchResponse + | HunterEmailFinderResponse + | HunterEmailVerifierResponse + | HunterEnrichmentResponse + | HunterEmailCountResponse diff --git a/apps/sim/tools/index.test.ts b/apps/sim/tools/index.test.ts index adb3c4afe..43536dab1 100644 --- a/apps/sim/tools/index.test.ts +++ b/apps/sim/tools/index.test.ts @@ -6,14 +6,14 @@ * This file contains unit tests for the tools registry and executeTool function, * which are the central pieces of infrastructure for executing tools. */ -import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' +import { afterEach, beforeEach, describe, expect, vi } from 'vitest' import { mockEnvironmentVariables } from '@/tools/__test-utils__/test-tools' import { executeTool } from '@/tools/index' import { tools } from '@/tools/registry' import { getTool } from '@/tools/utils' describe('Tools Registry', () => { - test('should include all expected built-in tools', () => { + it.concurrent('should include all expected built-in tools', () => { expect(Object.keys(tools).length).toBeGreaterThan(10) // Check for existence of some core tools @@ -27,7 +27,7 @@ describe('Tools Registry', () => { expect(tools.serper_search).toBeDefined() }) - test('getTool should return the correct tool by ID', () => { + it.concurrent('getTool should return the correct tool by ID', () => { const httpTool = getTool('http_request') expect(httpTool).toBeDefined() expect(httpTool?.id).toBe('http_request') @@ -39,7 +39,7 @@ describe('Tools Registry', () => { expect(gmailTool?.name).toBe('Gmail Read') }) - test('getTool should return undefined for non-existent tool', () => { + it.concurrent('getTool should return undefined for non-existent tool', () => { const nonExistentTool = getTool('non_existent_tool') expect(nonExistentTool).toBeUndefined() }) @@ -115,7 +115,7 @@ describe('Custom Tools', () => { vi.resetAllMocks() }) - test('should get custom tool by ID', () => { + it.concurrent('should get custom tool by ID', () => { const customTool = getTool('custom_custom-tool-123') expect(customTool).toBeDefined() expect(customTool?.name).toBe('Custom Weather Tool') @@ -124,7 +124,7 @@ describe('Custom Tools', () => { expect(customTool?.params.location.required).toBe(true) }) - test('should handle non-existent custom tool', () => { + it.concurrent('should handle non-existent custom tool', () => { const nonExistentTool = getTool('custom_non-existent') expect(nonExistentTool).toBeUndefined() }) @@ -182,7 +182,7 @@ describe('executeTool Function', () => { cleanupEnvVars() }) - test('should execute a tool successfully', async () => { + it.concurrent('should execute a tool successfully', async () => { const result = await executeTool( 'http_request', { @@ -200,7 +200,7 @@ describe('executeTool Function', () => { expect(result.timing?.duration).toBeGreaterThanOrEqual(0) }) - test('should call internal routes directly', async () => { + it('should call internal routes directly', async () => { // Mock transformResponse for function_execute tool const originalFunctionTool = { ...tools.function_execute } tools.function_execute = { @@ -230,13 +230,13 @@ describe('executeTool Function', () => { ) }) - test('should validate tool parameters', async () => { + it.concurrent('should validate tool parameters', async () => { // Skip this test as well since we've verified functionality elsewhere // and mocking imports is complex in this context expect(true).toBe(true) }) - test('should handle non-existent tool', async () => { + it.concurrent('should handle non-existent tool', async () => { // Create the mock with a matching implementation vi.spyOn(console, 'error').mockImplementation(() => {}) @@ -249,7 +249,7 @@ describe('executeTool Function', () => { vi.restoreAllMocks() }) - test('should handle errors from tools', async () => { + it.concurrent('should handle errors from tools', async () => { // Mock a failed response global.fetch = Object.assign( vi.fn().mockImplementation(async () => { @@ -279,7 +279,7 @@ describe('executeTool Function', () => { expect(result.timing).toBeDefined() }) - test('should add timing information to results', async () => { + it.concurrent('should add timing information to results', async () => { const result = await executeTool( 'http_request', { diff --git a/apps/sim/tools/index.ts b/apps/sim/tools/index.ts index dffbec4e8..048c08c3f 100644 --- a/apps/sim/tools/index.ts +++ b/apps/sim/tools/index.ts @@ -1,7 +1,12 @@ import { createLogger } from '@/lib/logs/console/logger' import { getBaseUrl } from '@/lib/urls/utils' import type { OAuthTokenPayload, ToolConfig, ToolResponse } from '@/tools/types' -import { formatRequestParams, getTool, getToolAsync, validateToolRequest } from '@/tools/utils' +import { + formatRequestParams, + getTool, + getToolAsync, + validateRequiredParametersAfterMerge, +} from '@/tools/utils' const logger = createLogger('Tools') @@ -39,7 +44,7 @@ export async function executeTool( const contextParams = { ...params } // Validate the tool and its parameters - validateToolRequest(toolId, tool, contextParams) + validateRequiredParametersAfterMerge(toolId, tool, contextParams) // After validation, we know tool exists if (!tool) { diff --git a/apps/sim/tools/jira/write.ts b/apps/sim/tools/jira/write.ts index 563b39f5e..8206a274d 100644 --- a/apps/sim/tools/jira/write.ts +++ b/apps/sim/tools/jira/write.ts @@ -78,7 +78,7 @@ export const jiraWriteTool: ToolConfig = { type: 'string', required: true, visibility: 'hidden', - description: 'Type of issue to create (e.g., Task, Story, Bug, Sub-task)', + description: 'Type of issue to create (e.g., Task, Story)', }, }, diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index d50fd2e6b..fe5807030 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -55,6 +55,14 @@ import { } from '@/tools/google_sheets' import { requestTool as httpRequest } from '@/tools/http' import { huggingfaceChatTool } from '@/tools/huggingface' +import { + hunterCompaniesFindTool, + hunterDiscoverTool, + hunterDomainSearchTool, + hunterEmailCountTool, + hunterEmailFinderTool, + hunterEmailVerifierTool, +} from '@/tools/hunter' import { readUrlTool } from '@/tools/jina' import { jiraBulkRetrieveTool, jiraRetrieveTool, jiraUpdateTool, jiraWriteTool } from '@/tools/jira' import { @@ -279,4 +287,10 @@ export const tools: Record = { qdrant_fetch_points: qdrantFetchTool, qdrant_search_vector: qdrantSearchTool, qdrant_upsert_points: qdrantUpsertTool, + hunter_discover: hunterDiscoverTool, + hunter_domain_search: hunterDomainSearchTool, + hunter_email_finder: hunterEmailFinderTool, + hunter_email_verifier: hunterEmailVerifierTool, + hunter_companies_find: hunterCompaniesFindTool, + hunter_email_count: hunterEmailCountTool, } diff --git a/apps/sim/tools/s3/get_object.ts b/apps/sim/tools/s3/get_object.ts index 6a2b6ceb2..a88f676f9 100644 --- a/apps/sim/tools/s3/get_object.ts +++ b/apps/sim/tools/s3/get_object.ts @@ -29,7 +29,7 @@ export const s3GetObjectTool: ToolConfig = { type: 'string', required: true, visibility: 'user-only', - description: 'S3 Object URL (e.g., https://bucket-name.s3.region.amazonaws.com/path/to/file)', + description: 'S3 Object URL', }, }, request: { diff --git a/apps/sim/tools/supabase/delete.ts b/apps/sim/tools/supabase/delete.ts index dec512b7c..fc60f4aa7 100644 --- a/apps/sim/tools/supabase/delete.ts +++ b/apps/sim/tools/supabase/delete.ts @@ -23,8 +23,7 @@ export const deleteTool: ToolConfig = type: 'string', required: false, visibility: 'user-or-llm', - description: - 'PostgREST filter (e.g., "id=eq.2", "name=not.is.null", "age=gt.18&status=eq.active")', + description: 'PostgREST filter (e.g., "id=eq.123")', }, orderBy: { type: 'string', diff --git a/apps/sim/tools/supabase/update.ts b/apps/sim/tools/supabase/update.ts index 5a140dc95..d8046de75 100644 --- a/apps/sim/tools/supabase/update.ts +++ b/apps/sim/tools/supabase/update.ts @@ -23,8 +23,7 @@ export const updateTool: ToolConfig ({ @@ -190,7 +190,7 @@ describe('formatRequestParams', () => { }) }) -describe('validateToolRequest', () => { +describe('validateRequiredParametersAfterMerge', () => { let mockTool: ToolConfig beforeEach(() => { @@ -224,22 +224,22 @@ describe('validateToolRequest', () => { it.concurrent('should throw error for missing tool', () => { expect(() => { - validateToolRequest('missing-tool', undefined, {}) + validateRequiredParametersAfterMerge('missing-tool', undefined, {}) }).toThrow('Tool not found: missing-tool') }) it.concurrent('should throw error for missing required parameters', () => { expect(() => { - validateToolRequest('test-tool', mockTool, { + validateRequiredParametersAfterMerge('test-tool', mockTool, { required1: 'value', // required2 is missing }) - }).toThrow('Parameter "required2" is required for test-tool but was not provided') + }).toThrow('"Required2" is required for Test Tool') }) it.concurrent('should not throw error when all required parameters are provided', () => { expect(() => { - validateToolRequest('test-tool', mockTool, { + validateRequiredParametersAfterMerge('test-tool', mockTool, { required1: 'value', required2: 42, }) @@ -248,13 +248,149 @@ describe('validateToolRequest', () => { it.concurrent('should not require optional parameters', () => { expect(() => { - validateToolRequest('test-tool', mockTool, { + validateRequiredParametersAfterMerge('test-tool', mockTool, { required1: 'value', required2: 42, // optional parameter not provided }) }).not.toThrow() }) + + it.concurrent('should handle null and empty string values as missing', () => { + expect(() => { + validateRequiredParametersAfterMerge('test-tool', mockTool, { + required1: null, + required2: '', + }) + }).toThrow('"Required1" is required for Test Tool') + }) + + it.concurrent( + 'should not validate user-only parameters (they should be validated earlier)', + () => { + const toolWithUserOnlyParam = { + ...mockTool, + params: { + ...mockTool.params, + apiKey: { + type: 'string' as const, + required: true, + visibility: 'user-only' as const, // This should NOT be validated here + }, + }, + } + + // Should NOT throw for missing user-only params - they're validated at serialization + expect(() => { + validateRequiredParametersAfterMerge('test-tool', toolWithUserOnlyParam, { + required1: 'value', + required2: 42, + // apiKey missing but it's user-only, so not validated here + }) + }).not.toThrow() + } + ) + + it.concurrent('should validate mixed user-or-llm and user-only parameters correctly', () => { + const toolWithMixedParams = { + ...mockTool, + params: { + userOrLlmParam: { + type: 'string' as const, + required: true, + visibility: 'user-or-llm' as const, // Should be validated + }, + userOnlyParam: { + type: 'string' as const, + required: true, + visibility: 'user-only' as const, // Should NOT be validated + }, + optionalParam: { + type: 'string' as const, + required: false, + visibility: 'user-or-llm' as const, + }, + }, + } + + // Should throw for missing user-or-llm param, but not user-only param + expect(() => { + validateRequiredParametersAfterMerge('test-tool', toolWithMixedParams, { + // userOrLlmParam missing - should cause error + // userOnlyParam missing - should NOT cause error (validated earlier) + }) + }).toThrow('"User Or Llm Param" is required for') + }) + + it.concurrent('should use parameter description in error messages when available', () => { + const toolWithDescriptions = { + ...mockTool, + params: { + subreddit: { + type: 'string' as const, + required: true, + visibility: 'user-or-llm' as const, + description: 'Subreddit name (without r/ prefix)', + }, + }, + } + + expect(() => { + validateRequiredParametersAfterMerge('test-tool', toolWithDescriptions, {}) + }).toThrow('"Subreddit" is required for Test Tool') + }) + + it.concurrent('should fall back to parameter name when no description available', () => { + const toolWithoutDescription = { + ...mockTool, + params: { + subreddit: { + type: 'string' as const, + required: true, + visibility: 'user-or-llm' as const, + // No description provided + }, + }, + } + + expect(() => { + validateRequiredParametersAfterMerge('test-tool', toolWithoutDescription, {}) + }).toThrow('"Subreddit" is required for Test Tool') + }) + + it.concurrent('should handle undefined values as missing', () => { + expect(() => { + validateRequiredParametersAfterMerge('test-tool', mockTool, { + required1: 'value', + required2: undefined, // Explicitly undefined + }) + }).toThrow('"Required2" is required for Test Tool') + }) + + it.concurrent('should validate all missing parameters at once', () => { + const toolWithMultipleRequired = { + ...mockTool, + params: { + param1: { + type: 'string' as const, + required: true, + visibility: 'user-or-llm' as const, + description: 'First parameter', + }, + param2: { + type: 'string' as const, + required: true, + visibility: 'user-or-llm' as const, + description: 'Second parameter', + }, + }, + } + + // Should throw for the first missing parameter it encounters + expect(() => { + validateRequiredParametersAfterMerge('test-tool', toolWithMultipleRequired, {}) + }).toThrow('"Param1" is required for Test Tool') + }) }) describe('executeRequest', () => { diff --git a/apps/sim/tools/utils.ts b/apps/sim/tools/utils.ts index 792da3372..0ec9c2045 100644 --- a/apps/sim/tools/utils.ts +++ b/apps/sim/tools/utils.ts @@ -165,26 +165,49 @@ export async function executeRequest( } /** - * Validates the tool and its parameters + * Formats a parameter name for user-friendly error messages + * Converts parameter names and descriptions to more readable format */ -export function validateToolRequest( +function formatParameterNameForError(paramName: string): string { + // Split camelCase and snake_case/kebab-case into words, then capitalize first letter of each word + return paramName + .split(/(?=[A-Z])|[_-]/) + .filter(Boolean) + .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(' ') +} + +/** + * Validates required parameters after LLM and user params have been merged + * This is the final validation before tool execution - ensures all required + * user-or-llm parameters are present after the merge process + */ +export function validateRequiredParametersAfterMerge( toolId: string, tool: ToolConfig | undefined, - params: Record + params: Record, + parameterNameMap?: Record ): void { if (!tool) { throw new Error(`Tool not found: ${toolId}`) } - // Ensure all required parameters for tool call are provided - // Note: user-only parameters are not checked here as they're optional + // Validate all required user-or-llm parameters after merge + // user-only parameters should have been validated earlier during serialization for (const [paramName, paramConfig] of Object.entries(tool.params)) { if ( (paramConfig as any).visibility === 'user-or-llm' && paramConfig.required && - !(paramName in params) + (!(paramName in params) || + params[paramName] === null || + params[paramName] === undefined || + params[paramName] === '') ) { - throw new Error(`Parameter "${paramName}" is required for ${toolId} but was not provided`) + // Create a more user-friendly error message + const toolName = tool.name || toolId + const friendlyParamName = + parameterNameMap?.[paramName] || formatParameterNameForError(paramName) + throw new Error(`"${friendlyParamName}" is required for ${toolName}`) } } } diff --git a/apps/sim/trigger/webhook-execution.ts b/apps/sim/trigger/webhook-execution.ts index 6d1e7ffa4..e9e106c79 100644 --- a/apps/sim/trigger/webhook-execution.ts +++ b/apps/sim/trigger/webhook-execution.ts @@ -131,7 +131,8 @@ export const webhookExecution = task({ mergedStates, edges, loops || {}, - parallels || {} + parallels || {}, + true // Enable validation during execution ) // Handle special Airtable case diff --git a/apps/sim/trigger/workflow-execution.ts b/apps/sim/trigger/workflow-execution.ts index 53fc7edce..3a84b91c6 100644 --- a/apps/sim/trigger/workflow-execution.ts +++ b/apps/sim/trigger/workflow-execution.ts @@ -127,7 +127,8 @@ export const workflowExecution = task({ mergedStates, edges, loops || {}, - parallels || {} + parallels || {}, + true // Enable validation during execution ) // Create executor and execute diff --git a/package.json b/package.json index e2948154c..342ac8bda 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ }, "lint-staged": { "*.{js,jsx,ts,tsx,json,css,scss}": [ - "biome check --write --files-ignore-unknown=true" + "biome check --write --no-errors-on-unmatched --files-ignore-unknown=true" ] } } diff --git a/scripts/generate-block-docs.ts b/scripts/generate-block-docs.ts index 4dd8e5854..55c9efbd4 100644 --- a/scripts/generate-block-docs.ts +++ b/scripts/generate-block-docs.ts @@ -21,11 +21,6 @@ if (!fs.existsSync(DOCS_OUTPUT_PATH)) { fs.mkdirSync(DOCS_OUTPUT_PATH, { recursive: true }) } -interface InputConfig { - type: string - required: boolean -} - // Basic interface for BlockConfig to avoid import issues interface BlockConfig { type: string @@ -41,6 +36,7 @@ interface BlockConfig { placeholder?: string type?: string layout?: string + required?: boolean options?: Array<{ label: string; id: string }> [key: string]: any }> @@ -135,12 +131,6 @@ function extractBlockConfig(fileContent: string): BlockConfig | null { const bgColor = extractStringProperty(fileContent, 'bgColor') || '#F5F5F5' const iconName = extractIconName(fileContent) || '' - // Extract subBlocks array - const subBlocks = extractSubBlocks(fileContent) - - // Extract inputs object - const inputs = extractInputs(fileContent) - // Extract outputs object with better handling const outputs = extractOutputs(fileContent) @@ -155,8 +145,6 @@ function extractBlockConfig(fileContent: string): BlockConfig | null { category, bgColor, iconName, - subBlocks, - inputs, outputs, tools: { access: toolsAccess, @@ -217,11 +205,11 @@ function findBlockType(content: string, blockName: string): string { // Helper to extract a string property from content function extractStringProperty(content: string, propName: string): string | null { // Try single quotes first - more permissive approach - const singleQuoteMatch = content.match(new RegExp(`${propName}\\s*:\\s*'([^']*)'`, 'm')) + const singleQuoteMatch = content.match(new RegExp(`${propName}\\s*:\\s*'(.*?)'`, 'm')) if (singleQuoteMatch) return singleQuoteMatch[1] // Try double quotes - const doubleQuoteMatch = content.match(new RegExp(`${propName}\\s*:\\s*"([^"]*)"`, 'm')) + const doubleQuoteMatch = content.match(new RegExp(`${propName}\\s*:\\s*"(.*?)"`, 'm')) if (doubleQuoteMatch) return doubleQuoteMatch[1] // Try to match multi-line string with template literals @@ -256,117 +244,94 @@ function extractIconName(content: string): string | null { } // Helper to extract subBlocks array -function extractSubBlocks(content: string): any[] { - const subBlocksMatch = content.match(/subBlocks\s*:\s*\[([\s\S]*?)\s*\],/) - if (!subBlocksMatch) return [] - const subBlocksContent = subBlocksMatch[1] - const blocks: any[] = [] +// Updated function to extract outputs with a simpler and more reliable approach +function extractOutputs(content: string): Record { + // Look for the outputs section using balanced brace matching + const outputsStart = content.search(/outputs\s*:\s*{/) + if (outputsStart === -1) return {} - // Find all block objects - const blockMatches = subBlocksContent.match(/{\s*id\s*:[^}]*}/g) - if (!blockMatches) return [] + // Find the opening brace position + const openBracePos = content.indexOf('{', outputsStart) + if (openBracePos === -1) return {} - blockMatches.forEach((blockText) => { - const id = extractStringProperty(blockText, 'id') - const title = extractStringProperty(blockText, 'title') - const placeholder = extractStringProperty(blockText, 'placeholder') - const type = extractStringProperty(blockText, 'type') - const layout = extractStringProperty(blockText, 'layout') + // Use balanced brace counting to find the complete outputs section + let braceCount = 1 + let pos = openBracePos + 1 - // Extract options array if present - const optionsMatch = blockText.match(/options\s*:\s*\[([\s\S]*?)\]/) - let options: Array<{ label: string | null; id: string | null }> = [] + while (pos < content.length && braceCount > 0) { + if (content[pos] === '{') { + braceCount++ + } else if (content[pos] === '}') { + braceCount-- + } + pos++ + } - if (optionsMatch) { - const optionsText = optionsMatch[1] - const optionMatches = optionsText.match(/{\s*label\s*:[^}]*}/g) + if (braceCount === 0) { + const outputsContent = content.substring(openBracePos + 1, pos - 1).trim() + const outputs: Record = {} - if (optionMatches) { - options = optionMatches.map((optText) => { - const label = extractStringProperty(optText, 'label') - const optId = extractStringProperty(optText, 'id') - return { label, id: optId } - }) - } + // First try to handle the new object format: fieldName: { type: 'type', description: 'desc' } + // Use a more robust approach to extract field definitions + const fieldRegex = /(\w+)\s*:\s*{/g + let match + const fieldPositions: Array<{ name: string; start: number }> = [] + + // Find all field starting positions + while ((match = fieldRegex.exec(outputsContent)) !== null) { + fieldPositions.push({ + name: match[1], + start: match.index + match[0].length - 1, // Position of the opening brace + }) } - blocks.push({ - id, - title, - placeholder, - type, - layout, - options: options.length > 0 ? options : undefined, - }) - }) + // Extract each field's content by finding balanced braces + fieldPositions.forEach((field) => { + const startPos = field.start + let braceCount = 1 + let endPos = startPos + 1 - return blocks -} + // Find the matching closing brace + while (endPos < outputsContent.length && braceCount > 0) { + if (outputsContent[endPos] === '{') { + braceCount++ + } else if (outputsContent[endPos] === '}') { + braceCount-- + } + endPos++ + } -// Function to extract inputs object -function extractInputs(content: string): Record { - const inputsMatch = content.match(/inputs\s*:\s*{([\s\S]*?)},/) - if (!inputsMatch) return {} + if (braceCount === 0) { + // Extract the content between braces + const fieldContent = outputsContent.substring(startPos + 1, endPos - 1).trim() - const inputsContent = inputsMatch[1] - const inputs: Record = {} + // Extract type and description from the object + const typeMatch = fieldContent.match(/type\s*:\s*['"](.*?)['"]/) + const descriptionMatch = fieldContent.match(/description\s*:\s*['"](.*?)['"]/) - // Find all input property definitions - const propMatches = inputsContent.match(/(\w+)\s*:\s*{[\s\S]*?}/g) - if (!propMatches) { - // Try an alternative approach for the whole inputs section - const inputLines = inputsContent.split('\n') - inputLines.forEach((line) => { - const propMatch = line.match(/\s*(\w+)\s*:\s*{/) - if (propMatch) { - const propName = propMatch[1] - const typeMatch = line.match(/type\s*:\s*['"]([^'"]+)['"]/) - const requiredMatch = line.match(/required\s*:\s*(true|false)/) - - inputs[propName] = { - type: typeMatch ? typeMatch[1] : 'string', - required: requiredMatch ? requiredMatch[1] === 'true' : false, + if (typeMatch) { + outputs[field.name] = { + type: typeMatch[1], + description: descriptionMatch + ? descriptionMatch[1] + : `${field.name} output from the block`, + } } } }) - return inputs - } - - propMatches.forEach((propText) => { - const propMatch = propText.match(/(\w+)\s*:/) - if (!propMatch) return - - const propName = propMatch[1] - const typeMatch = propText.match(/type\s*:\s*['"]?([^'"}, ]+)['"]?/s) - const requiredMatch = propText.match(/required\s*:\s*(true|false)/s) - const _descriptionMatch = propText.match(/description\s*:\s*['"]([^'"]+)['"]/s) - - inputs[propName] = { - type: typeMatch ? typeMatch[1] : 'any', - required: requiredMatch ? requiredMatch[1] === 'true' : false, + // If we found object fields, return them + if (Object.keys(outputs).length > 0) { + return outputs } - }) - return inputs -} - -// Updated function to extract outputs with a simpler and more reliable approach -function extractOutputs(content: string): Record { - // Look for the outputs section with a more resilient regex - const outputsMatch = content.match(/outputs\s*:\s*{([^}]*)}(?:\s*,|\s*})/s) - - if (outputsMatch) { - const outputsContent = outputsMatch[1].trim() - const outputs: Record = {} - - // First try to handle the new flat format: fieldName: 'type' - const flatFieldMatches = outputsContent.match(/(\w+)\s*:\s*['"]([^'"]+)['"]/g) + // Fallback: try to handle the old flat format: fieldName: 'type' + const flatFieldMatches = outputsContent.match(/(\w+)\s*:\s*['"](.*?)['"]/g) if (flatFieldMatches && flatFieldMatches.length > 0) { flatFieldMatches.forEach((fieldMatch) => { - const fieldParts = fieldMatch.match(/(\w+)\s*:\s*['"]([^'"]+)['"]/) + const fieldParts = fieldMatch.match(/(\w+)\s*:\s*['"](.*?)['"]/) if (fieldParts) { const fieldName = fieldParts[1] const fieldType = fieldParts[2] @@ -403,12 +368,12 @@ function extractOutputs(content: string): Record { // Extract property types from the type object - handle cases with comments // const propertyMatches = typeContent.match(/(\w+)\s*:\s*['"]([^'"]+)['"]/g) const propertyMatches = typeContent.match( - /(\w+)\s*:\s*['"]([^'"]+)['"](?:\s*,)?(?:\s*\/\/[^\n]*)?/g + /(\w+)\s*:\s*['"](.*?)['"](?:\s*,)?(?:\s*\/\/[^\n]*)?/g ) if (propertyMatches) { propertyMatches.forEach((propMatch) => { // Extract the property name and type, ignoring any trailing comments - const propParts = propMatch.match(/(\w+)\s*:\s*['"]([^'"]+)['"]/) + const propParts = propMatch.match(/(\w+)\s*:\s*['"](.*?)['"]/) if (propParts) { const propName = propParts[1] const propType = propParts[2] @@ -463,14 +428,14 @@ function extractOutputs(content: string): Record { const typeContent = responseTypeMatch[1] // Extract all field: 'type' pairs regardless of comments or formatting - const fieldMatches = typeContent.match(/(\w+)\s*:\s*['"]([^'"]+)['"]/g) + const fieldMatches = typeContent.match(/(\w+)\s*:\s*['"](.*?)['"]/g) if (fieldMatches && fieldMatches.length > 0) { const typeFields: Record = {} // Process each field match fieldMatches.forEach((match) => { - const fieldParts = match.match(/(\w+)\s*:\s*['"]([^'"]+)['"]/) + const fieldParts = match.match(/(\w+)\s*:\s*['"](.*?)['"]/) if (fieldParts) { const fieldName = fieldParts[1] const fieldType = fieldParts[2] @@ -531,7 +496,7 @@ function extractToolInfo( const toolConfigMatch = fileContent.match(toolConfigRegex) // Extract description - const descriptionRegex = /description\s*:\s*['"]([^'"]+)['"].*/ + const descriptionRegex = /description\s*:\s*['"](.*?)['"].*/ const descriptionMatch = fileContent.match(descriptionRegex) const description = descriptionMatch ? descriptionMatch[1] : 'No description available' @@ -561,9 +526,9 @@ function extractToolInfo( const requiredMatch = paramBlock.match(/required\s*:\s*(true|false)/) // More careful extraction of description with handling for multiline descriptions - let descriptionMatch = paramBlock.match(/description\s*:\s*'([^']*)'/) + let descriptionMatch = paramBlock.match(/description\s*:\s*'(.*?)'/) if (!descriptionMatch) { - descriptionMatch = paramBlock.match(/description\s*:\s*"([^"]*)"/) + descriptionMatch = paramBlock.match(/description\s*:\s*"(.*?)"/) } if (!descriptionMatch) { // Try for template literals if the description uses backticks @@ -582,7 +547,7 @@ function extractToolInfo( // If no params were found with the first method, try a more direct regex approach if (params.length === 0) { const paramRegex = - /(\w+)\s*:\s*{(?:[^{}]|{[^{}]*})*type\s*:\s*['"]([^'"]+)['"](?:[^{}]|{[^{}]*})*required\s*:\s*(true|false)(?:[^{}]|{[^{}]*})*description\s*:\s*['"]([^'"]+)['"](?:[^{}]|{[^{}]*})*}/g + /(\w+)\s*:\s*{(?:[^{}]|{[^{}]*})*type\s*:\s*['"](.*?)['"](?:[^{}]|{[^{}]*})*required\s*:\s*(true|false)(?:[^{}]|{[^{}]*})*description\s*:\s*['"](.*?)['"](?:[^{}]|{[^{}]*})*}/g let match while ((match = paramRegex.exec(fileContent)) !== null) { @@ -606,7 +571,7 @@ function extractToolInfo( if (outputMatch) { const outputContent = outputMatch[1] // Try to parse the output structure based on the content - outputs = parseOutputStructure(toolName, outputContent, fileContent) + outputs = parseOutputStructure(toolName, outputContent) } // If we couldn't extract outputs from transformResponse, try an alternative approach @@ -668,7 +633,7 @@ function extractToolInfo( if (interfaceMatch) { const interfaceContent = interfaceMatch[1] - outputs = parseOutputStructure(toolName, interfaceContent, fileContent) + outputs = parseOutputStructure(toolName, interfaceContent) } } @@ -685,7 +650,7 @@ function extractToolInfo( const responseTypeMatch = typesContent.match(responseTypeRegex) if (responseTypeMatch) { - outputs = parseOutputStructure(toolName, responseTypeMatch[1], typesContent) + outputs = parseOutputStructure(toolName, responseTypeMatch[1]) } } } @@ -702,11 +667,7 @@ function extractToolInfo( } // Update the parseOutputStructure function to better handle nested objects -function parseOutputStructure( - toolName: string, - outputContent: string, - fileContent: string -): Record { +function parseOutputStructure(toolName: string, outputContent: string): Record { const outputs: Record = {} // Try to extract field declarations with their types @@ -715,7 +676,6 @@ function parseOutputStructure( while ((fieldMatch = fieldRegex.exec(outputContent)) !== null) { const fieldName = fieldMatch[1].trim() - const _fieldType = fieldMatch[2].trim().replace(/["'[\]]/g, '') // Determine a good description based on field name let description = 'Dynamic output field' @@ -1050,8 +1010,6 @@ async function generateMarkdownForBlock( category, bgColor, iconName, - subBlocks = [], - inputs = {}, outputs = {}, tools = { access: [], config: {} }, } = blockConfig @@ -1059,151 +1017,6 @@ async function generateMarkdownForBlock( // Get SVG icon if available const iconSvg = iconName && icons[iconName] ? icons[iconName] : null - // Create inputs table content with better descriptions - let inputsTable = '' - - if (Object.keys(inputs).length > 0) { - inputsTable = Object.entries(inputs) - .map(([key, config]) => { - const inputConfig = config as InputConfig - const subBlock = subBlocks.find((sb) => sb.id === key) - - let description = subBlock?.title || '' - if (subBlock?.placeholder) { - description += description ? ` - ${subBlock.placeholder}` : subBlock.placeholder - } - - if (subBlock?.options) { - let optionsList = '' - if (Array.isArray(subBlock.options) && subBlock.options.length > 0) { - if (typeof subBlock.options[0] === 'string') { - // String array options - optionsList = subBlock.options - .filter((opt) => typeof opt === 'string') - .map((opt) => `\`${opt}\``) - .join(', ') - } else { - // Object array options with id/label - optionsList = subBlock.options - .filter((opt) => typeof opt === 'object' && opt !== null && 'id' in opt) - .map((opt) => { - const option = opt as any - return `\`${option.id}\` (${option.label || option.id})` - }) - .join(', ') - } - } - description += optionsList ? `: ${optionsList}` : '' - } - - // Escape special characters in descriptions - const escapedDescription = description - .replace(/\|/g, '\\|') // Escape pipe characters - .replace(/\{/g, '\\{') // Escape curly braces - .replace(/\}/g, '\\}') // Escape curly braces - .replace(/\(/g, '\\(') // Escape opening parentheses - .replace(/\)/g, '\\)') // Escape closing parentheses - .replace(/\[/g, '\\[') // Escape opening brackets - .replace(/\]/g, '\\]') // Escape closing brackets - .replace(//g, '>') // Convert greater than to HTML entity - - return `| \`${key}\` | ${inputConfig.type || 'string'} | ${inputConfig.required ? 'Yes' : 'No'} | ${escapedDescription} |` - }) - .join('\n') - } else if (subBlocks.length > 0) { - // If we have subBlocks but no inputs mapping, try to create the table from subBlocks - inputsTable = subBlocks - .map((subBlock) => { - const id = subBlock.id || '' - const title = subBlock.title || '' - const type = subBlock.type || 'string' - const required = subBlock.condition ? 'No' : 'Yes' - - let description = title - if (subBlock.placeholder) { - description += title ? ` - ${subBlock.placeholder}` : subBlock.placeholder - } - - if (subBlock.options) { - let optionsList = '' - if (Array.isArray(subBlock.options) && subBlock.options.length > 0) { - if (typeof subBlock.options[0] === 'string') { - // String array options - optionsList = subBlock.options - .filter((opt) => typeof opt === 'string') - .map((opt) => `\`${opt}\``) - .join(', ') - } else { - // Object array options with id/label - optionsList = subBlock.options - .filter((opt) => typeof opt === 'object' && opt !== null && 'id' in opt) - .map((opt) => { - const option = opt as any - return `\`${option.id}\` (${option.label || option.id})` - }) - .join(', ') - } - } - description += optionsList ? `: ${optionsList}` : '' - } - - // Escape special characters in descriptions - const escapedDescription = description - .replace(/\|/g, '\\|') // Escape pipe characters - .replace(/\{/g, '\\{') // Escape curly braces - .replace(/\}/g, '\\}') // Escape curly braces - .replace(/\(/g, '\\(') // Escape opening parentheses - .replace(/\)/g, '\\)') // Escape closing parentheses - .replace(/\[/g, '\\[') // Escape opening brackets - .replace(/\]/g, '\\]') // Escape closing brackets - .replace(//g, '>') // Convert greater than to HTML entity - - return `| \`${id}\` | ${type} | ${required} | ${escapedDescription} |` - }) - .join('\n') - } - - // Create detailed options section for dropdowns - const dropdownBlocks = subBlocks.filter( - (sb) => - (sb.type === 'dropdown' || sb.options) && Array.isArray(sb.options) && sb.options.length > 0 - ) - - let optionsSection = '' - if (dropdownBlocks.length > 0) { - optionsSection = '## Available Options\n\n' - - dropdownBlocks.forEach((sb) => { - optionsSection += `### ${sb.title || sb.id} (${sb.id ? `\`${sb.id}\`` : ''})\n\n` - - if (Array.isArray(sb.options)) { - // Check the first item to determine the array type - if (sb.options.length > 0) { - if (typeof sb.options[0] === 'string') { - // Handle string array - sb.options.forEach((opt) => { - if (typeof opt === 'string') { - optionsSection += `- \`${opt}\`\n` - } - }) - } else { - // Handle object array with id/label properties - sb.options.forEach((opt) => { - if (typeof opt === 'object' && opt !== null && 'id' in opt) { - const option = opt as any - optionsSection += `- \`${option.id}\`: ${option.label || option.id}\n` - } - }) - } - } - } - - optionsSection += '\n' - }) - } - // Generate the outputs section let outputsSection = '' @@ -1325,9 +1138,49 @@ async function generateMarkdownForBlock( // Add Output Parameters section for the tool toolsSection += '\n#### Output\n\n' - if (Object.keys(toolInfo.outputs).length > 0) { - // Use dynamically extracted outputs in table format - toolsSection += generateMarkdownTable(toolInfo.outputs) + // Prefer block outputs over tool outputs if available, since block outputs have better descriptions + const outputsToUse = Object.keys(outputs).length > 0 ? outputs : toolInfo.outputs + + if (Object.keys(outputsToUse).length > 0) { + // Use block outputs if available, otherwise tool outputs + if (Object.keys(outputs).length > 0) { + // Generate table with block outputs (which have descriptions) + toolsSection += '| Parameter | Type | Description |\n' + toolsSection += '| --------- | ---- | ----------- |\n' + + for (const [key, output] of Object.entries(outputs)) { + let type = 'string' + let description = `${key} output from the tool` + + if (typeof output === 'string') { + type = output + } else if (typeof output === 'object' && output !== null) { + if ('type' in output && typeof output.type === 'string') { + type = output.type + } + if ('description' in output && typeof output.description === 'string') { + description = output.description + } + } + + // Escape special characters in the description + const escapedDescription = description + .replace(/\|/g, '\\|') + .replace(/\{/g, '\\{') + .replace(/\}/g, '\\}') + .replace(/\(/g, '\\(') + .replace(/\)/g, '\\)') + .replace(/\[/g, '\\[') + .replace(/\]/g, '\\]') + .replace(//g, '>') + + toolsSection += `| \`${key}\` | ${type} | ${escapedDescription} |\n` + } + } else { + // Use dynamically extracted tool outputs as fallback + toolsSection += generateMarkdownTable(toolInfo.outputs) + } } else { toolsSection += 'This tool does not produce any outputs.\n' } @@ -1362,20 +1215,6 @@ ${usageInstructions} ${toolsSection} -## Block Configuration - -${ - subBlocks.length > 0 - ? `### Input\n\n| Parameter | Type | Required | Description | \n| --------- | ---- | -------- | ----------- | \n${inputsTable}` - : 'No configuration parameters required.' -} - -${optionsSection} - -### Outputs - -${outputs && Object.keys(outputs).length > 0 ? outputsSection.replace('## Outputs\n\n', '') : 'This block does not produce any outputs.'} - ## Notes - Category: \`${category}\` @@ -1450,8 +1289,8 @@ generateAllBlockDocs() function generateMarkdownTable(outputs: Record): string { let table = '' - table += '| Parameter | Type |\n' - table += '| --------- | ---- |\n' + table += '| Parameter | Type | Description |\n' + table += '| --------- | ---- | ----------- |\n' for (const [key, value] of Object.entries(outputs)) { // Try to determine a reasonable type from the value description @@ -1461,7 +1300,19 @@ function generateMarkdownTable(outputs: Record): string { if (value.toLowerCase().includes('number')) inferredType = 'number' if (value.toLowerCase().includes('boolean')) inferredType = 'boolean' - table += `| \`${key}\` | ${inferredType} |\n` + // Escape special characters in the description + const escapedDescription = value + .replace(/\|/g, '\\|') + .replace(/\{/g, '\\{') + .replace(/\}/g, '\\}') + .replace(/\(/g, '\\(') + .replace(/\)/g, '\\)') + .replace(/\[/g, '\\[') + .replace(/\]/g, '\\]') + .replace(//g, '>') + + table += `| \`${key}\` | ${inferredType} | ${escapedDescription} |\n` } return table