diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 0393dbf9f5..4d0d8df90d 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 b32f61ffab..3592883c5e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,42 +1,25 @@ -## Description - -Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. - -Fixes # (issue) - -## Type of change - -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) +## Summary +Brief description of what this PR does and why. + +Fixes #(issue) + +## Type of Change +- [ ] Bug fix +- [ ] New feature +- [ ] Breaking change +- [ ] Documentation +- [ ] Other: ___________ + +## Testing +How has this been tested? What should reviewers focus on? + +## 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/README.md b/README.md index 43e4d84c73..f9855815e9 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

License: Apache-2.0 Discord - Twitter + Twitter PRs welcome Documentation

diff --git a/apps/docs/content/docs/execution/advanced.mdx b/apps/docs/content/docs/execution/advanced.mdx index d510b676cb..a928211016 100644 --- a/apps/docs/content/docs/execution/advanced.mdx +++ b/apps/docs/content/docs/execution/advanced.mdx @@ -136,12 +136,18 @@ Sim automatically calculates costs for all AI model usage: ### How Costs Are Calculated +Every workflow execution includes two cost components: + +**Base Execution Charge**: $0.001 per execution + +**AI Model Usage**: Variable cost based on token consumption ```javascript -cost = (inputTokens × inputPrice + outputTokens × outputPrice) / 1,000,000 +modelCost = (inputTokens × inputPrice + outputTokens × outputPrice) / 1,000,000 +totalCost = baseExecutionCharge + modelCost ``` - Prices are per million tokens. The calculation divides by 1,000,000 to get the actual cost. + AI model prices are per million tokens. The calculation divides by 1,000,000 to get the actual cost. Workflows without AI blocks only incur the base execution charge. ### Pricing Options diff --git a/apps/docs/content/docs/tools/airtable.mdx b/apps/docs/content/docs/tools/airtable.mdx index 9fb44da30a..a078806f03 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 b6d14c260b..b18140b41f 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 2d91c0d2e4..2063625a6e 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 4e83223b7f..6320eabb0a 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 d69b157c4a..311f3d7e24 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 bfde043819..674e834e3e 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 11d77ca193..2e0d6a7708 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 88acd2f9e6..34e860ff2c 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 398bb1bf2a..f7afb49c4e 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 ec90e7ccc0..ba28ff956f 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 e0298fbb54..b9b48839af 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 1e3335f844..a12d6cc67b 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 b54309ec60..eb02695622 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 274bf5c3cc..cfac1a14d4 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 2ba25bfd2a..d729ff6d52 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 34ed4a2a6f..b98863031e 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 3534386646..00909a3608 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 f01d65f10b..6e0875a4ec 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 0000000000..abab69c5c2 --- /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 5e005969c4..2614231f44 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 d3921c0e2c..7daef77f54 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 3e750a03fa..19973d3731 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 ea9f20f1a5..b42ce28fb9 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 4374a96389..a541add5a0 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 5c0e4b6dec..24c15bb1a9 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 ac060f7169..6c9448d051 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 1a236dba57..fcd46da35a 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 a047b4a7bd..db0fc6c075 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 b915b262c3..15ecd8e28d 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 a1ae20df57..51e80b0113 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 b26d96ca86..eb4700322f 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 6a48f5f490..51801ce370 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 647cdeecfa..b8d04338bd 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 f4382d1482..6e1873b646 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 797f45f726..b8855fdde2 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 58bcb2bfb5..76baccb1ae 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 73d9f0deeb..fcc2c478bf 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 ef80acf0e7..22d16546a8 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 ade38b20d9..f8d62e2f7b 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 30964438f2..5332f90022 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 65c0563269..e04ce7b3a7 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 d16b88893a..074e9c1d18 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 d2534b74a1..c0fe0e6ca6 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 40ed4a7ac7..3f1b5f20b5 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 9ff0f2e244..772967fc25 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 292e1d310e..3f4ec5b605 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 fe1fa32859..155d82edfe 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 ed5f5a70de..917b8340b2 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 0c92a8d715..27bfa26680 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 c3c310db0e..dfbd529f26 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 2b550dc00c..21550595b2 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 c638dd2eea..cf1ea47ab2 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 7050095355..d8cc291c52 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 25fad519fc..66b557d544 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 29517bdc03..a8b3e96750 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 4568159ee7..6556945e57 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 326a6d4586..e6c99d0451 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 75235d7777..6f8ddda348 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/(landing)/components/sections/footer.tsx b/apps/sim/app/(landing)/components/sections/footer.tsx index 21d285487d..c4a0ff6bbb 100644 --- a/apps/sim/app/(landing)/components/sections/footer.tsx +++ b/apps/sim/app/(landing)/components/sections/footer.tsx @@ -159,7 +159,7 @@ function Footer() { { expect(result3.error).toBe('Email not authorized') }) }) + + describe('Execution Result Processing', () => { + it('should process logs regardless of overall success status', () => { + // Test that logs are processed even when overall execution fails + // This is key for partial success scenarios + const executionResult = { + success: false, // Overall execution failed + output: {}, + logs: [ + { + blockId: 'agent1', + startedAt: '2023-01-01T00:00:00Z', + endedAt: '2023-01-01T00:00:01Z', + durationMs: 1000, + success: true, + output: { content: 'Agent 1 succeeded' }, + error: undefined, + }, + { + blockId: 'agent2', + startedAt: '2023-01-01T00:00:00Z', + endedAt: '2023-01-01T00:00:01Z', + durationMs: 500, + success: false, + output: null, + error: 'Agent 2 failed', + }, + ], + metadata: { duration: 1000 }, + } + + // Test the key logic: logs should be processed regardless of overall success + expect(executionResult.success).toBe(false) + expect(executionResult.logs).toBeDefined() + expect(executionResult.logs).toHaveLength(2) + + // First log should be successful + expect(executionResult.logs[0].success).toBe(true) + expect(executionResult.logs[0].output?.content).toBe('Agent 1 succeeded') + + // Second log should be failed + expect(executionResult.logs[1].success).toBe(false) + expect(executionResult.logs[1].error).toBe('Agent 2 failed') + }) + + it('should handle ExecutionResult vs StreamingExecution types correctly', () => { + const executionResult = { + success: true, + output: { content: 'test' }, + logs: [], + metadata: { duration: 100 }, + } + + // Test direct ExecutionResult + const directResult = executionResult + const extractedDirect = directResult + expect(extractedDirect).toBe(executionResult) + + // Test StreamingExecution with embedded ExecutionResult + const streamingResult = { + stream: new ReadableStream(), + execution: executionResult, + } + + // Simulate the type extraction logic from executeWorkflowForChat + const extractedFromStreaming = + streamingResult && typeof streamingResult === 'object' && 'execution' in streamingResult + ? streamingResult.execution + : streamingResult + + expect(extractedFromStreaming).toBe(executionResult) + }) + }) }) diff --git a/apps/sim/app/api/chat/utils.ts b/apps/sim/app/api/chat/utils.ts index 6d66bf749d..5143ea79c2 100644 --- a/apps/sim/app/api/chat/utils.ts +++ b/apps/sim/app/api/chat/utils.ts @@ -10,10 +10,11 @@ import { hasAdminPermission } from '@/lib/permissions/utils' import { processStreamingBlockLogs } from '@/lib/tokenization' import { getEmailDomain } from '@/lib/urls/utils' import { decryptSecret } from '@/lib/utils' +import { getBlock } from '@/blocks' import { db } from '@/db' import { chat, environment as envTable, userStats, workflow } from '@/db/schema' import { Executor } from '@/executor' -import type { BlockLog } from '@/executor/types' +import type { BlockLog, ExecutionResult } from '@/executor/types' import { Serializer } from '@/serializer' import { mergeSubblockState } from '@/stores/workflows/server-utils' import type { WorkflowState } from '@/stores/workflows/workflow/types' @@ -423,7 +424,22 @@ export async function executeWorkflowForChat( // Prepare for execution, similar to use-workflow-execution.ts const mergedStates = mergeSubblockState(blocks) - const currentBlockStates = Object.entries(mergedStates).reduce( + + const filteredStates = Object.entries(mergedStates).reduce( + (acc, [id, block]) => { + const blockConfig = getBlock(block.type) + const isTriggerBlock = blockConfig?.category === 'triggers' + + // Skip trigger blocks during chat execution + if (!isTriggerBlock) { + acc[id] = block + } + return acc + }, + {} as typeof mergedStates + ) + + const currentBlockStates = Object.entries(filteredStates).reduce( (acc, [id, block]) => { acc[id] = Object.entries(block.subBlocks).reduce( (subAcc, [key, subBlock]) => { @@ -465,12 +481,23 @@ export async function executeWorkflowForChat( logger.warn(`[${requestId}] Could not parse workflow variables:`, error) } - // Create serialized workflow + // Filter edges to exclude connections to/from trigger blocks (same as manual execution) + const triggerBlockIds = Object.keys(mergedStates).filter((id) => { + const blockConfig = getBlock(mergedStates[id].type) + return blockConfig?.category === 'triggers' + }) + + const filteredEdges = edges.filter( + (edge) => !triggerBlockIds.includes(edge.source) && !triggerBlockIds.includes(edge.target) + ) + + // Create serialized workflow with filtered blocks and edges const serializedWorkflow = new Serializer().serializeWorkflow( - mergedStates, - edges, + filteredStates, + filteredEdges, loops, - parallels + parallels, + true // Enable validation during execution ) // Decrypt environment variables @@ -522,6 +549,7 @@ export async function executeWorkflowForChat( async start(controller) { const encoder = new TextEncoder() const streamedContent = new Map() + const streamedBlocks = new Set() // Track which blocks have started streaming const onStream = async (streamingExecution: any): Promise => { if (!streamingExecution.stream) return @@ -530,6 +558,15 @@ export async function executeWorkflowForChat( const reader = streamingExecution.stream.getReader() if (blockId) { streamedContent.set(blockId, '') + + // Add separator if this is not the first block to stream + if (streamedBlocks.size > 0) { + // Send separator before the new block starts + controller.enqueue( + encoder.encode(`data: ${JSON.stringify({ blockId, chunk: '\n\n' })}\n\n`) + ) + } + streamedBlocks.add(blockId) } try { while (true) { @@ -561,7 +598,7 @@ export async function executeWorkflowForChat( contextExtensions: { stream: true, selectedOutputIds: selectedOutputIds.length > 0 ? selectedOutputIds : outputBlockIds, - edges: edges.map((e: any) => ({ + edges: filteredEdges.map((e: any) => ({ source: e.source, target: e.target, })), @@ -588,25 +625,117 @@ export async function executeWorkflowForChat( throw error } - if (result && 'success' in result) { - // Update streamed content and apply tokenization - if (result.logs) { - result.logs.forEach((log: BlockLog) => { - if (streamedContent.has(log.blockId)) { - const content = streamedContent.get(log.blockId) - if (log.output) { - log.output.content = content + // Handle both ExecutionResult and StreamingExecution types + const executionResult = + result && typeof result === 'object' && 'execution' in result + ? (result.execution as ExecutionResult) + : (result as ExecutionResult) + + if (executionResult?.logs) { + // Update streamed content and apply tokenization - process regardless of overall success + // This ensures partial successes (some agents succeed, some fail) still return results + + // Add newlines between different agent outputs for better readability + const processedOutputs = new Set() + executionResult.logs.forEach((log: BlockLog) => { + if (streamedContent.has(log.blockId)) { + const content = streamedContent.get(log.blockId) + if (log.output && content) { + // Add newline separation between different outputs (but not before the first one) + const separator = processedOutputs.size > 0 ? '\n\n' : '' + log.output.content = separator + content + processedOutputs.add(log.blockId) + } + } + }) + + // Also process non-streamed outputs from selected blocks (like function blocks) + // This uses the same logic as the chat panel to ensure identical behavior + const nonStreamingLogs = executionResult.logs.filter( + (log: BlockLog) => !streamedContent.has(log.blockId) + ) + + // Extract the exact same functions used by the chat panel + const extractBlockIdFromOutputId = (outputId: string): string => { + return outputId.includes('_') ? outputId.split('_')[0] : outputId.split('.')[0] + } + + const extractPathFromOutputId = (outputId: string, blockId: string): string => { + return outputId.substring(blockId.length + 1) + } + + const parseOutputContentSafely = (output: any): any => { + if (!output?.content) { + return output + } + + if (typeof output.content === 'string') { + try { + return JSON.parse(output.content) + } catch (e) { + // Fallback to original structure if parsing fails + return output + } + } + + return output + } + + // Filter outputs that have matching logs (exactly like chat panel) + const outputsToRender = selectedOutputIds.filter((outputId) => { + const blockIdForOutput = extractBlockIdFromOutputId(outputId) + return nonStreamingLogs.some((log) => log.blockId === blockIdForOutput) + }) + + // Process each selected output (exactly like chat panel) + for (const outputId of outputsToRender) { + const blockIdForOutput = extractBlockIdFromOutputId(outputId) + const path = extractPathFromOutputId(outputId, blockIdForOutput) + const log = nonStreamingLogs.find((l) => l.blockId === blockIdForOutput) + + if (log) { + let outputValue: any = log.output + + if (path) { + // Parse JSON content safely (exactly like chat panel) + outputValue = parseOutputContentSafely(outputValue) + + const pathParts = path.split('.') + for (const part of pathParts) { + if (outputValue && typeof outputValue === 'object' && part in outputValue) { + outputValue = outputValue[part] + } else { + outputValue = undefined + break + } } } - }) - // Process all logs for streaming tokenization - const processedCount = processStreamingBlockLogs(result.logs, streamedContent) - logger.info(`[CHAT-API] Processed ${processedCount} blocks for streaming tokenization`) + if (outputValue !== undefined) { + // Add newline separation between different outputs + const separator = processedOutputs.size > 0 ? '\n\n' : '' + + // Format the output exactly like the chat panel + const formattedOutput = + typeof outputValue === 'string' ? outputValue : JSON.stringify(outputValue, null, 2) + + // Update the log content + if (!log.output.content) { + log.output.content = separator + formattedOutput + } else { + log.output.content = separator + formattedOutput + } + processedOutputs.add(log.blockId) + } + } } - const { traceSpans, totalDuration } = buildTraceSpans(result) - const enrichedResult = { ...result, traceSpans, totalDuration } + // Process all logs for streaming tokenization + const processedCount = processStreamingBlockLogs(executionResult.logs, streamedContent) + logger.info(`Processed ${processedCount} blocks for streaming tokenization`) + + const { traceSpans, totalDuration } = buildTraceSpans(executionResult) + const enrichedResult = { ...executionResult, traceSpans, totalDuration } if (conversationId) { if (!enrichedResult.metadata) { enrichedResult.metadata = { @@ -619,7 +748,7 @@ export async function executeWorkflowForChat( const executionId = uuidv4() logger.debug(`Generated execution ID for deployed chat: ${executionId}`) - if (result.success) { + if (executionResult.success) { try { await db .update(userStats) @@ -642,12 +771,12 @@ export async function executeWorkflowForChat( } // Complete logging session (for both success and failure) - if (result && 'success' in result) { - const { traceSpans } = buildTraceSpans(result) + if (executionResult?.logs) { + const { traceSpans } = buildTraceSpans(executionResult) await loggingSession.safeComplete({ endedAt: new Date().toISOString(), - totalDurationMs: result.metadata?.duration || 0, - finalOutput: result.output, + totalDurationMs: executionResult.metadata?.duration || 0, + finalOutput: executionResult.output, traceSpans, }) } diff --git a/apps/sim/app/api/codegen/route.test.ts b/apps/sim/app/api/codegen/route.test.ts deleted file mode 100644 index 94d95d457a..0000000000 --- a/apps/sim/app/api/codegen/route.test.ts +++ /dev/null @@ -1,390 +0,0 @@ -/** - * Tests for codegen API route - * - * @vitest-environment node - */ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { createMockRequest } from '@/app/api/__test-utils__/utils' - -describe('Codegen API Route', () => { - const mockOpenAI = { - chat: { - completions: { - create: vi.fn(), - }, - }, - } - const mockLogger = { - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - debug: vi.fn(), - } - const mockEnv = { - OPENAI_API_KEY: 'test-api-key', - } - - const mockUUID = 'mock-uuid-12345678-90ab-cdef-1234-567890abcdef' - - beforeEach(() => { - vi.resetModules() - mockEnv.OPENAI_API_KEY = 'test-api-key' - - vi.stubGlobal('crypto', { - randomUUID: vi.fn().mockReturnValue(mockUUID), - }) - - const MockAPIError = class extends Error { - status: number - constructor(message: string, status?: number) { - super(message) - this.status = status || 500 - } - } - - vi.doMock('openai', () => ({ - default: vi.fn().mockImplementation(() => mockOpenAI), - APIError: MockAPIError, - })) - - vi.doMock('@/lib/env', () => ({ - env: mockEnv, - })) - - vi.doMock('@/lib/logs/console/logger', () => ({ - createLogger: vi.fn().mockReturnValue(mockLogger), - })) - - vi.doMock('next/cache', () => ({ - unstable_noStore: vi.fn(), - })) - }) - - afterEach(() => { - vi.clearAllMocks() - }) - - it('should generate JSON schema successfully', async () => { - const mockResponse = { - choices: [ - { - message: { - content: JSON.stringify({ - name: 'test_function', - description: 'A test function', - strict: true, - schema: { - type: 'object', - properties: { - input: { type: 'string', description: 'Test input' }, - }, - additionalProperties: false, - required: ['input'], - }, - }), - }, - }, - ], - } - - mockOpenAI.chat.completions.create.mockResolvedValueOnce(mockResponse) - - const req = createMockRequest('POST', { - prompt: 'Create a function that takes a string input', - generationType: 'json-schema', - }) - - const { POST } = await import('@/app/api/codegen/route') - - const response = await POST(req) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.success).toBe(true) - expect(data.generatedContent).toBeDefined() - expect(() => JSON.parse(data.generatedContent)).not.toThrow() - expect(mockOpenAI.chat.completions.create).toHaveBeenCalledWith({ - model: 'gpt-4o', - messages: expect.arrayContaining([ - expect.objectContaining({ role: 'system' }), - expect.objectContaining({ - role: 'user', - content: 'Create a function that takes a string input', - }), - ]), - temperature: 0.2, - max_tokens: 1500, - response_format: { type: 'json_object' }, - }) - }) - - it('should generate JavaScript function body successfully', async () => { - const mockResponse = { - choices: [ - { - message: { - content: 'const input = ;\nreturn input.toUpperCase();', - }, - }, - ], - } - - mockOpenAI.chat.completions.create.mockResolvedValueOnce(mockResponse) - - const req = createMockRequest('POST', { - prompt: 'Convert input to uppercase', - generationType: 'javascript-function-body', - }) - - const { POST } = await import('@/app/api/codegen/route') - - const response = await POST(req) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.success).toBe(true) - expect(data.generatedContent).toBe('const input = ;\nreturn input.toUpperCase();') - expect(mockOpenAI.chat.completions.create).toHaveBeenCalledWith({ - model: 'gpt-4o', - messages: expect.arrayContaining([ - expect.objectContaining({ role: 'system' }), - expect.objectContaining({ role: 'user' }), - ]), - temperature: 0.2, - max_tokens: 1500, - response_format: undefined, - }) - }) - - it('should generate custom tool schema successfully', async () => { - const mockResponse = { - choices: [ - { - message: { - content: JSON.stringify({ - type: 'function', - function: { - name: 'testFunction', - description: 'A test function', - parameters: { - type: 'object', - properties: { - input: { type: 'string', description: 'Test input' }, - }, - required: ['input'], - additionalProperties: false, - }, - }, - }), - }, - }, - ], - } - - mockOpenAI.chat.completions.create.mockResolvedValueOnce(mockResponse) - - const req = createMockRequest('POST', { - prompt: 'Create a custom tool for testing', - generationType: 'custom-tool-schema', - }) - - const { POST } = await import('@/app/api/codegen/route') - - const response = await POST(req) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data.success).toBe(true) - expect(data.generatedContent).toBeDefined() - }) - - it('should include context in the prompt', async () => { - const mockResponse = { - choices: [ - { - message: { - content: 'const result = ;\nreturn result;', - }, - }, - ], - } - - mockOpenAI.chat.completions.create.mockResolvedValueOnce(mockResponse) - - const req = createMockRequest('POST', { - prompt: 'Modify this function', - generationType: 'javascript-function-body', - context: 'existing function code here', - }) - - const { POST } = await import('@/app/api/codegen/route') - - const response = await POST(req) - - expect(response.status).toBe(200) - expect(mockOpenAI.chat.completions.create).toHaveBeenCalledWith({ - model: 'gpt-4o', - messages: expect.arrayContaining([ - expect.objectContaining({ role: 'system' }), - expect.objectContaining({ - role: 'user', - content: - 'Prompt: Modify this function\\n\\nExisting Content/Context:\\nexisting function code here', - }), - ]), - temperature: 0.2, - max_tokens: 1500, - response_format: undefined, - }) - }) - - it('should include conversation history', async () => { - const mockResponse = { - choices: [ - { - message: { - content: 'Updated function code', - }, - }, - ], - } - - mockOpenAI.chat.completions.create.mockResolvedValueOnce(mockResponse) - - const req = createMockRequest('POST', { - prompt: 'Update the function', - generationType: 'javascript-function-body', - history: [ - { role: 'user', content: 'Create a function' }, - { role: 'assistant', content: 'function created' }, - ], - }) - - const { POST } = await import('@/app/api/codegen/route') - - const response = await POST(req) - - expect(response.status).toBe(200) - expect(mockOpenAI.chat.completions.create).toHaveBeenCalledWith({ - model: 'gpt-4o', - messages: expect.arrayContaining([ - expect.objectContaining({ role: 'system' }), - expect.objectContaining({ role: 'user', content: 'Create a function' }), - expect.objectContaining({ role: 'assistant', content: 'function created' }), - expect.objectContaining({ role: 'user', content: 'Update the function' }), - ]), - temperature: 0.2, - max_tokens: 1500, - response_format: undefined, - }) - }) - - it('should handle missing OpenAI API key', async () => { - mockEnv.OPENAI_API_KEY = '' - - const req = createMockRequest('POST', { - prompt: 'Test prompt', - generationType: 'json-schema', - }) - - const { POST } = await import('@/app/api/codegen/route') - - const response = await POST(req) - const data = await response.json() - - expect(response.status).toBe(503) - expect(data.success).toBe(false) - expect(data.error).toBe('Code generation service is not configured.') - }) - - it('should handle missing required fields', async () => { - const req = createMockRequest('POST', { - prompt: '', - }) - - const { POST } = await import('@/app/api/codegen/route') - - const response = await POST(req) - const data = await response.json() - - expect(response.status).toBe(400) - expect(data.success).toBe(false) - expect(data.error).toBe('Missing required fields: prompt and generationType.') - expect(mockLogger.warn).toHaveBeenCalled() - }) - - it('should handle invalid generation type', async () => { - const req = createMockRequest('POST', { - prompt: 'Test prompt', - generationType: 'invalid-type', - }) - - const { POST } = await import('@/app/api/codegen/route') - - const response = await POST(req) - const data = await response.json() - - expect(response.status).toBe(400) - expect(data.success).toBe(false) - expect(data.error).toBe('Invalid generationType: invalid-type') - expect(mockLogger.warn).toHaveBeenCalled() - }) - - it('should handle empty OpenAI response', async () => { - const mockResponse = { - choices: [ - { - message: { - content: null, - }, - }, - ], - } - - mockOpenAI.chat.completions.create.mockResolvedValueOnce(mockResponse) - - const req = createMockRequest('POST', { - prompt: 'Test prompt', - generationType: 'javascript-function-body', - }) - - const { POST } = await import('@/app/api/codegen/route') - - const response = await POST(req) - const data = await response.json() - - expect(response.status).toBe(500) - expect(data.success).toBe(false) - expect(data.error).toBe('Failed to generate content. OpenAI response was empty.') - expect(mockLogger.error).toHaveBeenCalled() - }) - - it('should handle invalid JSON schema generation', async () => { - const mockResponse = { - choices: [ - { - message: { - content: 'invalid json content', - }, - }, - ], - } - - mockOpenAI.chat.completions.create.mockResolvedValueOnce(mockResponse) - - const req = createMockRequest('POST', { - prompt: 'Create a schema', - generationType: 'json-schema', - }) - - const { POST } = await import('@/app/api/codegen/route') - - const response = await POST(req) - const data = await response.json() - - expect(response.status).toBe(500) - expect(data.success).toBe(false) - expect(data.error).toBe('Generated JSON schema was invalid.') - expect(mockLogger.error).toHaveBeenCalled() - }) -}) diff --git a/apps/sim/app/api/codegen/route.ts b/apps/sim/app/api/codegen/route.ts deleted file mode 100644 index 8fd3eec1a2..0000000000 --- a/apps/sim/app/api/codegen/route.ts +++ /dev/null @@ -1,535 +0,0 @@ -import { unstable_noStore as noStore } from 'next/cache' -import { type NextRequest, NextResponse } from 'next/server' -import OpenAI from 'openai' -import { env } from '@/lib/env' -import { createLogger } from '@/lib/logs/console/logger' - -export const dynamic = 'force-dynamic' -export const runtime = 'edge' -export const maxDuration = 60 - -const logger = createLogger('GenerateCodeAPI') - -const openai = env.OPENAI_API_KEY - ? new OpenAI({ - apiKey: env.OPENAI_API_KEY, - }) - : null - -if (!env.OPENAI_API_KEY) { - logger.warn('OPENAI_API_KEY not found. Code generation API will not function.') -} - -type GenerationType = - | 'json-schema' - | 'javascript-function-body' - | 'typescript-function-body' - | 'custom-tool-schema' - | 'json-object' - -// Define the structure for a single message in the history -interface ChatMessage { - role: 'user' | 'assistant' | 'system' // System role might be needed if we include the initial system prompt in history - content: string -} - -interface RequestBody { - prompt: string - generationType: GenerationType - context?: string - stream?: boolean - history?: ChatMessage[] // Optional conversation history -} - -const systemPrompts: Record = { - 'json-schema': `You are an expert programmer specializing in creating JSON schemas according to a specific format. -Generate ONLY the JSON schema based on the user's request. -The output MUST be a single, valid JSON object, starting with { and ending with }. -The JSON object MUST have the following top-level properties: 'name' (string), 'description' (string), 'strict' (boolean, usually true), and 'schema' (object). -The 'schema' object must define the structure and MUST contain 'type': 'object', 'properties': {...}, 'additionalProperties': false, and 'required': [...]. -Inside 'properties', use standard JSON Schema properties (type, description, enum, items for arrays, etc.). -Do not include any explanations, markdown formatting, or other text outside the JSON object. - -Valid Schema Examples: - -Example 1: -{ - "name": "reddit_post", - "description": "Fetches the reddit posts in the given subreddit", - "strict": true, - "schema": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The title of the post" - }, - "content": { - "type": "string", - "description": "The content of the post" - } - }, - "additionalProperties": false, - "required": [ "title", "content" ] - } -} - -Example 2: -{ - "name": "get_weather", - "description": "Fetches the current weather for a specific location.", - "strict": true, - "schema": { - "type": "object", - "properties": { - "location": { - "type": "string", - "description": "The city and state, e.g., San Francisco, CA" - }, - "unit": { - "type": "string", - "description": "Temperature unit", - "enum": ["celsius", "fahrenheit"] - } - }, - "additionalProperties": false, - "required": ["location", "unit"] - } -} - -Example 3 (Array Input): -{ - "name": "process_items", - "description": "Processes a list of items with specific IDs.", - "strict": true, - "schema": { - "type": "object", - "properties": { - "item_ids": { - "type": "array", - "description": "A list of unique item identifiers to process.", - "items": { - "type": "string", - "description": "An item ID" - } - }, - "processing_mode": { - "type": "string", - "description": "The mode for processing", - "enum": ["fast", "thorough"] - } - }, - "additionalProperties": false, - "required": ["item_ids", "processing_mode"] - } -} -`, - 'custom-tool-schema': `You are an expert programmer specializing in creating OpenAI function calling format JSON schemas for custom tools. -Generate ONLY the JSON schema based on the user's request. -The output MUST be a single, valid JSON object, starting with { and ending with }. -The JSON schema MUST follow this specific format: -1. Top-level property "type" must be set to "function" -2. A "function" object containing: - - "name": A concise, camelCase name for the function - - "description": A clear description of what the function does - - "parameters": A JSON Schema object describing the function's parameters with: - - "type": "object" - - "properties": An object containing parameter definitions - - "required": An array of required parameter names - -Do not include any explanations, markdown formatting, or other text outside the JSON object. - -Valid Schema Examples: - -Example 1: -{ - "type": "function", - "function": { - "name": "getWeather", - "description": "Fetches the current weather for a specific location.", - "parameters": { - "type": "object", - "properties": { - "location": { - "type": "string", - "description": "The city and state, e.g., San Francisco, CA" - }, - "unit": { - "type": "string", - "description": "Temperature unit", - "enum": ["celsius", "fahrenheit"] - } - }, - "required": ["location"], - "additionalProperties": false - } - } -} - -Example 2: -{ - "type": "function", - "function": { - "name": "addItemToOrder", - "description": "Add one quantity of a food item to the order.", - "parameters": { - "type": "object", - "properties": { - "itemName": { - "type": "string", - "description": "The name of the food item to add to order" - }, - "quantity": { - "type": "integer", - "description": "The quantity of the item to add", - "default": 1 - } - }, - "required": ["itemName"], - "additionalProperties": false - } - } -} - -Example 3 (Array Input): -{ - "type": "function", - "function": { - "name": "processItems", - "description": "Processes a list of items with specific IDs.", - "parameters": { - "type": "object", - "properties": { - "itemIds": { - "type": "array", - "description": "A list of unique item identifiers to process.", - "items": { - "type": "string", - "description": "An item ID" - } - }, - "processingMode": { - "type": "string", - "description": "The mode for processing", - "enum": ["fast", "thorough"] - } - }, - "required": ["itemIds"], - "additionalProperties": false - } - } -} -`, - 'javascript-function-body': `You are an expert JavaScript programmer. -Generate ONLY the raw body of a JavaScript function based on the user's request. -The code should be executable within an 'async function(params, environmentVariables) {...}' context. -- 'params' (object): Contains input parameters derived from the JSON schema. Access these directly using the parameter name wrapped in angle brackets, e.g., ''. Do NOT use 'params.paramName'. -- 'environmentVariables' (object): Contains environment variables. Reference these using the double curly brace syntax: '{{ENV_VAR_NAME}}'. Do NOT use 'environmentVariables.VAR_NAME' or env. - -IMPORTANT FORMATTING RULES: -1. Reference Environment Variables: Use the exact syntax {{VARIABLE_NAME}}. Do NOT wrap it in quotes (e.g., use 'apiKey = {{SERVICE_API_KEY}}' not 'apiKey = "{{SERVICE_API_KEY}}"'). Our system replaces these placeholders before execution. -2. Reference Input Parameters/Workflow Variables: Use the exact syntax . Do NOT wrap it in quotes (e.g., use 'userId = ;' not 'userId = "";'). This includes parameters defined in the block's schema and outputs from previous blocks. -3. Function Body ONLY: Do NOT include the function signature (e.g., 'async function myFunction() {' or the surrounding '}'). -4. Imports: Do NOT include import/require statements unless they are standard Node.js built-in modules (e.g., 'crypto', 'fs'). External libraries are not supported in this context. -5. Output: Ensure the code returns a value if the function is expected to produce output. Use 'return'. -6. Clarity: Write clean, readable code. -7. No Explanations: Do NOT include markdown formatting, comments explaining the rules, or any text other than the raw JavaScript code for the function body. - -Example Scenario: -User Prompt: "Fetch user data from an API. Use the User ID passed in as 'userId' and an API Key stored as the 'SERVICE_API_KEY' environment variable." - -Generated Code: -const userId = ; // Correct: Accessing input parameter without quotes -const apiKey = {{SERVICE_API_KEY}}; // Correct: Accessing environment variable without quotes -const url = \`https://api.example.com/users/\${userId}\`; - -try { - const response = await fetch(url, { - method: 'GET', - headers: { - 'Authorization': \`Bearer \${apiKey}\`, - 'Content-Type': 'application/json' - } - }); - - if (!response.ok) { - // Throwing an error will mark the block execution as failed - throw new Error(\`API request failed with status \${response.status}: \${await response.text()}\`); - } - - const data = await response.json(); - console.log('User data fetched successfully.'); // Optional: logging for debugging - return data; // Return the fetched data which becomes the block's output -} catch (error) { - console.error(\`Error fetching user data: \${error.message}\`); - // Re-throwing the error ensures the workflow knows this step failed. - throw error; -}`, - 'typescript-function-body': `You are an expert TypeScript programmer. -Generate ONLY the body of a TypeScript function based on the user's request. -The code should be executable within an async context. You have access to a 'params' object (typed as Record) containing input parameters and an 'environmentVariables' object (typed as Record) for env vars. -Do not include the function signature (e.g., 'async function myFunction(): Promise {'). -Do not include import/require statements unless absolutely necessary and they are standard Node.js modules. -Do not include markdown formatting or explanations. -Output only the raw TypeScript code. Use modern TypeScript features where appropriate. Do not use semicolons. -Example: -const userId = as string -const apiKey = {{SERVICE_API_KEY}} -const response = await fetch(\`https://api.example.com/users/\${userId}\`, { headers: { Authorization: \`Bearer \${apiKey}\` } }) -if (!response.ok) { - throw new Error(\`Failed to fetch user data: \${response.statusText}\`) -} -const data: unknown = await response.json() -// Add type checking/assertion if necessary -return data // Ensure you return a value if expected`, - - 'json-object': `You are an expert JSON programmer. -Generate ONLY the raw JSON object based on the user's request. -The output MUST be a single, valid JSON object, starting with { and ending with }. - -Do not include any explanations, markdown formatting, or other text outside the JSON object. - -You have access to the following variables you can use to generate the JSON body: -- 'params' (object): Contains input parameters derived from the JSON schema. Access these directly using the parameter name wrapped in angle brackets, e.g., ''. Do NOT use 'params.paramName'. -- 'environmentVariables' (object): Contains environment variables. Reference these using the double curly brace syntax: '{{ENV_VAR_NAME}}'. Do NOT use 'environmentVariables.VAR_NAME' or env. - -Example: -{ - "name": "", - "age": , - "success": true -} -`, -} - -export async function POST(req: NextRequest) { - const requestId = crypto.randomUUID().slice(0, 8) - logger.info(`[${requestId}] Received code generation request`) - - if (!openai) { - logger.error(`[${requestId}] OpenAI client not initialized. Missing API key.`) - return NextResponse.json( - { success: false, error: 'Code generation service is not configured.' }, - { status: 503 } - ) - } - - try { - const body = (await req.json()) as RequestBody - noStore() - - // Destructure history along with other fields - const { prompt, generationType, context, stream = false, history = [] } = body - - if (!prompt || !generationType) { - logger.warn(`[${requestId}] Invalid request: Missing prompt or generationType.`) - return NextResponse.json( - { success: false, error: 'Missing required fields: prompt and generationType.' }, - { status: 400 } - ) - } - - if (!systemPrompts[generationType]) { - logger.warn(`[${requestId}] Invalid generationType: ${generationType}`) - return NextResponse.json( - { success: false, error: `Invalid generationType: ${generationType}` }, - { status: 400 } - ) - } - - const systemPrompt = systemPrompts[generationType] - - // Construct the user message, potentially including context - const currentUserMessageContent = context - ? `Prompt: ${prompt}\\n\\nExisting Content/Context:\\n${context}` - : `${prompt}` // Keep it simple for follow-ups, context is in history - - // Prepare messages for OpenAI API - // Start with the system prompt - const messages: ChatMessage[] = [{ role: 'system', content: systemPrompt }] - - // Add previous messages from history - // Filter out any potential system messages from history if we always prepend a fresh one - messages.push(...history.filter((msg) => msg.role !== 'system')) - - // Add the current user prompt - messages.push({ role: 'user', content: currentUserMessageContent }) - - logger.debug(`[${requestId}] Calling OpenAI API`, { - generationType, - stream, - historyLength: history.length, - }) - - // For streaming responses - if (stream) { - try { - const streamCompletion = await openai?.chat.completions.create({ - model: 'gpt-4o', - messages: messages, - temperature: 0.2, - max_tokens: 1500, - stream: true, - }) - - // Use ReadableStream for Edge runtime - return new Response( - new ReadableStream({ - async start(controller) { - const encoder = new TextEncoder() - let fullContent = generationType === 'json-schema' ? '' : undefined - - // Process each chunk - for await (const chunk of streamCompletion) { - const content = chunk.choices[0]?.delta?.content || '' - if (content) { - // Only append if fullContent is defined (i.e., for json-schema) - if (fullContent !== undefined) { - fullContent += content - } - - // Send the chunk to the client - controller.enqueue( - encoder.encode( - `${JSON.stringify({ - chunk: content, - done: false, - })}\n` - ) - ) - } - } - - // Check JSON validity for json-schema type when streaming is complete - if (generationType === 'json-schema' && fullContent) { - try { - JSON.parse(fullContent) - } catch (parseError: any) { - logger.error(`[${requestId}] Generated JSON schema is invalid`, { - error: parseError.message, - content: fullContent, - }) - - // Send error to client - controller.enqueue( - encoder.encode( - `${JSON.stringify({ - error: 'Generated JSON schema was invalid.', - done: true, - })}\n` - ) - ) - controller.close() - return - } - } - - // Send the final done message - controller.enqueue( - encoder.encode( - `${JSON.stringify({ - done: true, - ...(fullContent !== undefined && { fullContent: fullContent }), - })}\n` - ) - ) - controller.close() - logger.info(`[${requestId}] Code generation streaming completed`, { generationType }) - }, - }), - { - headers: { - 'Content-Type': 'text/event-stream', - 'Cache-Control': 'no-cache, no-transform', - Connection: 'keep-alive', - }, - } - ) - } catch (error: any) { - logger.error(`[${requestId}] Streaming error`, { - error: error.message || 'Unknown error', - stack: error.stack, - }) - - return NextResponse.json( - { success: false, error: 'An error occurred during code generation streaming.' }, - { status: 500 } - ) - } - } - - // For non-streaming responses (original implementation) - const completion = await openai?.chat.completions.create({ - // Use non-null assertion - model: 'gpt-4o', - // Pass the constructed messages array - messages: messages, - temperature: 0.2, - max_tokens: 1500, - response_format: generationType === 'json-schema' ? { type: 'json_object' } : undefined, - }) - - const generatedContent = completion.choices[0]?.message?.content?.trim() - - if (!generatedContent) { - logger.error(`[${requestId}] OpenAI response was empty or invalid.`) - return NextResponse.json( - { success: false, error: 'Failed to generate content. OpenAI response was empty.' }, - { status: 500 } - ) - } - - logger.info(`[${requestId}] Code generation successful`, { generationType }) - - if (generationType === 'json-schema') { - try { - JSON.parse(generatedContent) - return NextResponse.json({ success: true, generatedContent }) - } catch (parseError: any) { - logger.error(`[${requestId}] Generated JSON schema is invalid`, { - error: parseError.message, - content: generatedContent, - }) - return NextResponse.json( - { success: false, error: 'Generated JSON schema was invalid.' }, - { status: 500 } - ) - } - } else { - return NextResponse.json({ success: true, generatedContent }) - } - } catch (error: any) { - logger.error(`[${requestId}] Code generation failed`, { - error: error.message || 'Unknown error', - stack: error.stack, - }) - - let clientErrorMessage = 'Code generation failed. Please try again later.' - // Keep original message for server logging - let serverErrorMessage = error.message || 'Unknown error' - - let status = 500 - if (error instanceof OpenAI.APIError) { - status = error.status || 500 - serverErrorMessage = error.message // Use specific API error for server logs - logger.error(`[${requestId}] OpenAI API Error: ${status} - ${serverErrorMessage}`) - // Optionally, customize client message based on status, but keep it generic - if (status === 401) { - clientErrorMessage = 'Authentication failed. Please check your API key configuration.' - } else if (status === 429) { - clientErrorMessage = 'Rate limit exceeded. Please try again later.' - } else if (status >= 500) { - clientErrorMessage = - 'The code generation service is currently unavailable. Please try again later.' - } - } - - return NextResponse.json( - { - success: false, - error: clientErrorMessage, - }, - { status } - ) - } -} diff --git a/apps/sim/app/api/proxy/route.ts b/apps/sim/app/api/proxy/route.ts index e40dc91cf2..95c6d99975 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 1b0b3799e8..0f6cd28389 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/telemetry/route.ts b/apps/sim/app/api/telemetry/route.ts index a9738adcc9..5b7b44664f 100644 --- a/apps/sim/app/api/telemetry/route.ts +++ b/apps/sim/app/api/telemetry/route.ts @@ -86,7 +86,7 @@ async function forwardToCollector(data: any): Promise { return false } - const endpoint = env.TELEMETRY_ENDPOINT || 'https://telemetry.sim.ai/v1/traces' + const endpoint = env.TELEMETRY_ENDPOINT || 'https://telemetry.simstudio.ai/v1/traces' const timeout = DEFAULT_TIMEOUT try { diff --git a/apps/sim/app/api/wand-generate/route.ts b/apps/sim/app/api/wand-generate/route.ts new file mode 100644 index 0000000000..d7eeba5be0 --- /dev/null +++ b/apps/sim/app/api/wand-generate/route.ts @@ -0,0 +1,194 @@ +import { unstable_noStore as noStore } from 'next/cache' +import { type NextRequest, NextResponse } from 'next/server' +import OpenAI from 'openai' +import { env } from '@/lib/env' +import { createLogger } from '@/lib/logs/console/logger' + +export const dynamic = 'force-dynamic' +export const runtime = 'edge' +export const maxDuration = 60 + +const logger = createLogger('WandGenerateAPI') + +const openai = env.OPENAI_API_KEY + ? new OpenAI({ + apiKey: env.OPENAI_API_KEY, + }) + : null + +if (!env.OPENAI_API_KEY) { + logger.warn('OPENAI_API_KEY not found. Wand generation API will not function.') +} + +interface ChatMessage { + role: 'user' | 'assistant' | 'system' + content: string +} + +interface RequestBody { + prompt: string + systemPrompt?: string + stream?: boolean + history?: ChatMessage[] +} + +// The endpoint is now generic - system prompts come from wand configs + +export async function POST(req: NextRequest) { + const requestId = crypto.randomUUID().slice(0, 8) + logger.info(`[${requestId}] Received wand generation request`) + + if (!openai) { + logger.error(`[${requestId}] OpenAI client not initialized. Missing API key.`) + return NextResponse.json( + { success: false, error: 'Wand generation service is not configured.' }, + { status: 503 } + ) + } + + try { + noStore() + const body = (await req.json()) as RequestBody + + const { prompt, systemPrompt, stream = false, history = [] } = body + + if (!prompt) { + logger.warn(`[${requestId}] Invalid request: Missing prompt.`) + return NextResponse.json( + { success: false, error: 'Missing required field: prompt.' }, + { status: 400 } + ) + } + + // Use provided system prompt or default + const finalSystemPrompt = + systemPrompt || + 'You are a helpful AI assistant. Generate content exactly as requested by the user.' + + // Prepare messages for OpenAI API + const messages: ChatMessage[] = [{ role: 'system', content: finalSystemPrompt }] + + // Add previous messages from history + messages.push(...history.filter((msg) => msg.role !== 'system')) + + // Add the current user prompt + messages.push({ role: 'user', content: prompt }) + + logger.debug(`[${requestId}] Calling OpenAI API for wand generation`, { + stream, + historyLength: history.length, + }) + + // For streaming responses + if (stream) { + try { + const streamCompletion = await openai?.chat.completions.create({ + model: 'gpt-4o', + messages: messages, + temperature: 0.3, + max_tokens: 10000, + stream: true, + }) + + return new Response( + new ReadableStream({ + async start(controller) { + const encoder = new TextEncoder() + + try { + for await (const chunk of streamCompletion) { + const content = chunk.choices[0]?.delta?.content || '' + if (content) { + // Use the same format as codegen API for consistency + controller.enqueue( + encoder.encode(`${JSON.stringify({ chunk: content, done: false })}\n`) + ) + } + } + + // Send completion signal + controller.enqueue(encoder.encode(`${JSON.stringify({ chunk: '', done: true })}\n`)) + controller.close() + logger.info(`[${requestId}] Wand generation streaming completed`) + } catch (streamError: any) { + logger.error(`[${requestId}] Streaming error`, { error: streamError.message }) + controller.enqueue( + encoder.encode(`${JSON.stringify({ error: 'Streaming failed', done: true })}\n`) + ) + controller.close() + } + }, + }), + { + headers: { + 'Content-Type': 'text/plain', + 'Cache-Control': 'no-cache, no-transform', + Connection: 'keep-alive', + }, + } + ) + } catch (error: any) { + logger.error(`[${requestId}] Streaming error`, { + error: error.message || 'Unknown error', + stack: error.stack, + }) + + return NextResponse.json( + { success: false, error: 'An error occurred during wand generation streaming.' }, + { status: 500 } + ) + } + } + + // For non-streaming responses + const completion = await openai?.chat.completions.create({ + model: 'gpt-4o', + messages: messages, + temperature: 0.3, + max_tokens: 10000, + }) + + const generatedContent = completion.choices[0]?.message?.content?.trim() + + if (!generatedContent) { + logger.error(`[${requestId}] OpenAI response was empty or invalid.`) + return NextResponse.json( + { success: false, error: 'Failed to generate content. OpenAI response was empty.' }, + { status: 500 } + ) + } + + logger.info(`[${requestId}] Wand generation successful`) + return NextResponse.json({ success: true, content: generatedContent }) + } catch (error: any) { + logger.error(`[${requestId}] Wand generation failed`, { + error: error.message || 'Unknown error', + stack: error.stack, + }) + + let clientErrorMessage = 'Wand generation failed. Please try again later.' + let status = 500 + + if (error instanceof OpenAI.APIError) { + status = error.status || 500 + logger.error(`[${requestId}] OpenAI API Error: ${status} - ${error.message}`) + + if (status === 401) { + clientErrorMessage = 'Authentication failed. Please check your API key configuration.' + } else if (status === 429) { + clientErrorMessage = 'Rate limit exceeded. Please try again later.' + } else if (status >= 500) { + clientErrorMessage = + 'The wand generation service is currently unavailable. Please try again later.' + } + } + + return NextResponse.json( + { + success: false, + error: clientErrorMessage, + }, + { status } + ) + } +} diff --git a/apps/sim/app/api/workflows/[id]/execute/route.ts b/apps/sim/app/api/workflows/[id]/execute/route.ts index bb19cefa2e..893a5efa68 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/chat/[subdomain]/hooks/use-chat-streaming.ts b/apps/sim/app/chat/[subdomain]/hooks/use-chat-streaming.ts index 67718c489c..9bad3adf1e 100644 --- a/apps/sim/app/chat/[subdomain]/hooks/use-chat-streaming.ts +++ b/apps/sim/app/chat/[subdomain]/hooks/use-chat-streaming.ts @@ -3,6 +3,8 @@ import { useRef, useState } from 'react' import { createLogger } from '@/lib/logs/console/logger' import type { ChatMessage } from '@/app/chat/[subdomain]/components/message/message' +// No longer need complex output extraction - backend handles this +import type { ExecutionResult } from '@/executor/types' const logger = createLogger('UseChatStreaming') @@ -96,6 +98,8 @@ export function useChatStreaming() { let accumulatedText = '' let lastAudioPosition = 0 + // Track which blocks have streamed content (like chat panel) + const messageIdMap = new Map() const messageId = crypto.randomUUID() setMessages((prev) => [ ...prev, @@ -148,13 +152,49 @@ export function useChatStreaming() { const { blockId, chunk: contentChunk, event: eventType } = json if (eventType === 'final' && json.data) { + // The backend has already processed and combined all outputs + // We just need to extract the combined content and use it + const result = json.data as ExecutionResult + + // Collect all content from logs that have output.content (backend processed) + let combinedContent = '' + if (result.logs) { + const contentParts: string[] = [] + + // Get content from all logs that have processed content + result.logs.forEach((log) => { + if (log.output?.content && typeof log.output.content === 'string') { + // The backend already includes proper separators, so just collect the content + contentParts.push(log.output.content) + } + }) + + // Join without additional separators since backend already handles this + combinedContent = contentParts.join('') + } + + // Update the existing streaming message with the final combined content setMessages((prev) => - prev.map((msg) => (msg.id === messageId ? { ...msg, isStreaming: false } : msg)) + prev.map((msg) => + msg.id === messageId + ? { + ...msg, + content: combinedContent || accumulatedText, // Use combined content or fallback to streamed + isStreaming: false, + } + : msg + ) ) + return } if (blockId && contentChunk) { + // Track that this block has streamed content (like chat panel) + if (!messageIdMap.has(blockId)) { + messageIdMap.set(blockId, messageId) + } + accumulatedText += contentChunk setMessages((prev) => prev.map((msg) => diff --git a/apps/sim/app/globals.css b/apps/sim/app/globals.css index 27d1b85a1a..aa95668db2 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 8bfae54320..ccb474fd94 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 @@ -6,10 +6,6 @@ import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Label } from '@/components/ui/label' import { createLogger } from '@/lib/logs/console/logger' -import { - type DocumentTag, - DocumentTagEntry, -} from '@/app/workspace/[workspaceId]/knowledge/components/document-tag-entry/document-tag-entry' import { useKnowledgeUpload } from '@/app/workspace/[workspaceId]/knowledge/hooks/use-knowledge-upload' const logger = createLogger('UploadModal') @@ -50,7 +46,7 @@ export function UploadModal({ }: UploadModalProps) { const fileInputRef = useRef(null) const [files, setFiles] = useState([]) - const [tags, setTags] = useState([]) + const [fileError, setFileError] = useState(null) const [isDragging, setIsDragging] = useState(false) @@ -66,7 +62,6 @@ export function UploadModal({ if (isUploading) return // Prevent closing during upload setFiles([]) - setTags([]) setFileError(null) setIsDragging(false) onOpenChange(false) @@ -145,23 +140,7 @@ export function UploadModal({ if (files.length === 0) return try { - // Convert DocumentTag array to TagData format - const tagData: Record = {} - tags.forEach((tag) => { - if (tag.value.trim()) { - tagData[tag.slot] = tag.value.trim() - } - }) - - // Create files with tags for upload - const filesWithTags = files.map((file) => { - // Add tags as custom properties to the file object - const fileWithTags = file as unknown as File & Record - Object.assign(fileWithTags, tagData) - return fileWithTags - }) - - await uploadFiles(filesWithTags, knowledgeBaseId, { + await uploadFiles(files, knowledgeBaseId, { chunkSize: chunkingConfig?.maxSize || 1024, minCharactersPerChunk: chunkingConfig?.minSize || 100, chunkOverlap: chunkingConfig?.overlap || 200, @@ -180,15 +159,6 @@ export function UploadModal({
- {/* Document Tag Entry Section */} - - {/* 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 ab3aa91d1a..1eab551645 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,32 @@ '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 { + Badge, + Button, + Dialog, + DialogContent, + DialogHeader, + DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu' -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' -import { + Input, + Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from '@/components/ui/select' +} 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,205 +34,197 @@ 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) - const { tagDefinitions: kbTagDefinitions, fetchTagDefinitions: refreshTagDefinitions } = - useKnowledgeBaseTagDefinitions(knowledgeBaseId) - - const [editingTag, setEditingTag] = useState<{ - index: number - value: string - tagName: string - isNew: boolean - } | null>(null) + // Use different hooks based on whether we have a documentId + const documentTagHook = useTagDefinitions(knowledgeBaseId, documentId) + const kbTagHook = useKnowledgeBaseTagDefinitions(knowledgeBaseId) + + // Use the document-level hook since we have documentId + const { saveTagDefinitions } = documentTagHook + const { tagDefinitions: kbTagDefinitions, fetchTagDefinitions: refreshTagDefinitions } = kbTagHook + + // Modal state for tag editing + const [editingTagIndex, setEditingTagIndex] = useState(null) + const [modalOpen, setModalOpen] = useState(false) + const [editForm, setEditForm] = useState({ + displayName: '', + fieldType: 'text', + value: '', + }) const getNextAvailableSlot = (): DocumentTag['slot'] => { - const usedSlots = new Set(tags.map((tag) => tag.slot)) + // Check which slots are used at the KB level (tag definitions) + const usedSlots = new Set(kbTagDefinitions.map((def) => def.tagSlot)) for (const slot of TAG_SLOTS) { if (!usedSlots.has(slot)) { return slot } } - return 'tag1' // fallback - } - - 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) - } + return TAG_SLOTS[0] // Fallback to first slot if all are used } - const handleCleanupUnusedTags = async () => { - if (!knowledgeBaseId || !documentId) return - - try { - const response = await fetch( - `/api/knowledge/${knowledgeBaseId}/documents/${documentId}/tag-definitions?action=cleanup`, - { - method: 'DELETE', - } - ) + const handleRemoveTag = async (index: number) => { + const updatedTags = tags.filter((_, i) => i !== index) + onTagsChange(updatedTags) - if (!response.ok) { - throw new Error(`Cleanup failed: ${response.statusText}`) + // Persist the changes if onSave is provided + if (onSave) { + try { + await onSave(updatedTags) + } catch (error) { + // Handle error silently - the UI will show the optimistic update + // but the user can retry if needed } - - 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 }) + // Open modal to edit tag + const openTagModal = (index: number) => { + const tag = tags[index] + setEditingTagIndex(index) + setEditForm({ + displayName: tag.displayName, + fieldType: tag.fieldType, + value: tag.value, + }) + setModalOpen(true) } - 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 newTag: DocumentTag = { - slot: getNextAvailableSlot(), - displayName: tagName.trim(), - fieldType: fieldType, - value: value.trim(), - } + // Open modal to create new tag + const openNewTagModal = () => { + setEditingTagIndex(null) + setEditForm({ + displayName: '', + fieldType: 'text', + value: '', + }) + setModalOpen(true) + } - const updatedTags = [...tags, newTag] + // Save tag from modal + const saveTagFromModal = async () => { + if (!editForm.displayName.trim()) return - // SIMPLE ATOMIC OPERATION - NO CLEANUP try { - // 1. Save tag definition first - await handleSaveDefinitions(updatedTags) - - // 2. Save document values - if (onSave) { - await onSave(updatedTags) + // Calculate slot once at the beginning + const targetSlot = + editingTagIndex !== null ? tags[editingTagIndex].slot : getNextAvailableSlot() + + if (editingTagIndex !== null) { + // Editing existing tag - use existing slot + const updatedTags = [...tags] + updatedTags[editingTagIndex] = { + ...updatedTags[editingTagIndex], + displayName: editForm.displayName, + fieldType: editForm.fieldType, + value: editForm.value, + } + onTagsChange(updatedTags) + } else { + // Creating new tag - use calculated slot + const newTag: DocumentTag = { + slot: targetSlot, + displayName: editForm.displayName, + fieldType: editForm.fieldType, + value: editForm.value, + } + const newTags = [...tags, newTag] + onTagsChange(newTags) } - // 3. Update UI - onTagsChange(updatedTags) - } catch (error) { - console.error('Failed to save tag:', error) - alert(`Failed to save tag "${tagName}". Please try again.`) - } - } - - const handleUpdateTag = async (index: number, newValue: string) => { - if (!newValue.trim()) return + // Auto-save tag definition if it's a new name + const existingDefinition = kbTagDefinitions.find( + (def) => def.displayName.toLowerCase() === editForm.displayName.toLowerCase() + ) - const updatedTags = tags.map((tag, i) => - i === index ? { ...tag, value: newValue.trim() } : tag - ) + if (!existingDefinition) { + // Use the same slot for both tag and definition + const newDefinition: TagDefinitionInput = { + displayName: editForm.displayName, + fieldType: editForm.fieldType, + tagSlot: targetSlot as TagSlot, + } - // SIMPLE ATOMIC OPERATION - NO CLEANUP - try { - // 1. Save document values - if (onSave) { - await onSave(updatedTags) + if (saveTagDefinitions) { + await saveTagDefinitions([newDefinition]) + } else { + throw new Error('Cannot save tag definitions without a document ID') + } + await refreshTagDefinitions() } - // 2. Save tag definitions - await handleSaveDefinitions(updatedTags) - // 3. Update UI - onTagsChange(updatedTags) - } catch (error) { - console.error('Failed to update tag:', 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...') + // Save the actual document tags if onSave is provided if (onSave) { + const updatedTags = + editingTagIndex !== null + ? tags.map((tag, index) => + index === editingTagIndex + ? { + ...tag, + displayName: editForm.displayName, + fieldType: editForm.fieldType, + value: editForm.value, + } + : tag + ) + : [ + ...tags, + { + slot: targetSlot, + displayName: editForm.displayName, + fieldType: editForm.fieldType, + value: editForm.value, + }, + ] 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) - } + setModalOpen(false) + } catch (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 */} +
+
+

Document Tags

+
+ + {/* Tags as Badges */}
{tags.map((tag, index) => ( -
- setEditingTag({ index, value: tag.value, tagName: tag.displayName, isNew: false }) - } + openTagModal(index)} > - {tag.displayName}: - {tag.value} + {tag.displayName || 'Unnamed Tag'} + {tag.value && ( + <> + : + {tag.value} + + )} -
+ ))} -
- {/* Add Tag Dropdown Selector */} - - - - - - {/* 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 -
- )} - - - - {/* 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) - }} - /> - )} + {/* Add Tag Button */} + +
- {/* Tag count display */} - {kbTagDefinitions.length > 0 && ( -
- {kbTagDefinitions.length} of {MAX_TAG_SLOTS} tag slots used in this knowledge base + {tags.length === 0 && ( +
+

+ No tags added yet. Click "Add Tag" to get started. +

)} -
- ) -} -// 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() - } - } +
+ {kbTagDefinitions.length} of {MAX_TAG_SLOTS} tag slots used +
- 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' - /> + {/* Tag Edit Modal */} + + + + {editingTagIndex !== null ? 'Edit Tag' : 'Add New Tag'} + + +
+ {/* Tag Name */} +
+ +
+ setEditForm({ ...editForm, displayName: e.target.value })} + placeholder='Enter tag name' + className='flex-1' + /> + {availableDefinitions.length > 0 && ( + + + + + + {availableDefinitions.map((def) => ( + + setEditForm({ + ...editForm, + displayName: def.displayName, + fieldType: def.fieldType, + }) + } + > + {def.displayName} + + ))} + + + )} +
- )} - {/* Type Selection for New Tags */} - {isNew && ( -
- - setEditForm({ ...editForm, fieldType: value })} + > + @@ -421,35 +326,29 @@ function EditTagModal({
- )} - {/* Value Input */} -
- - setValue(e.target.value)} - onKeyDown={handleKeyDown} - placeholder='Enter tag value' - className='mt-1 text-sm' - /> + {/* Tag Value */} +
+ + setEditForm({ ...editForm, value: e.target.value })} + placeholder='Enter tag value' + /> +
-
- -
- -
+
+
) } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/components/chat-deploy/chat-deploy.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/components/chat-deploy/chat-deploy.tsx index 505a8d7ed0..150591aa3b 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/components/chat-deploy/chat-deploy.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/components/chat-deploy/chat-deploy.tsx @@ -1,20 +1,10 @@ 'use client' -import { type FormEvent, useEffect, useRef, useState } from 'react' -import { - AlertTriangle, - Check, - Copy, - Eye, - EyeOff, - Loader2, - Plus, - RefreshCw, - Trash2, -} from 'lucide-react' -import { z } from 'zod' -import { Alert, AlertDescription } from '@/components/ui/alert' +import { useEffect, useRef, useState } from 'react' +import { AlertTriangle, Loader2 } from 'lucide-react' import { + Alert, + AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, @@ -23,118 +13,74 @@ import { AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, -} from '@/components/ui/alert-dialog' -import { Button } from '@/components/ui/button' -import { Card, CardContent } from '@/components/ui/card' -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' -import { Skeleton } from '@/components/ui/skeleton' -import { Textarea } from '@/components/ui/textarea' + Card, + CardContent, + ImageUpload, + Input, + Label, + Skeleton, + Textarea, +} from '@/components/ui' import { createLogger } from '@/lib/logs/console/logger' -import { getBaseDomain, getEmailDomain } from '@/lib/urls/utils' -import { cn } from '@/lib/utils' -import { ImageSelector } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/components/image-selector/image-selector' +import { getEmailDomain } from '@/lib/urls/utils' +import { AuthSelector } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/components/chat-deploy/components/auth-selector' +import { SubdomainInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/components/chat-deploy/components/subdomain-input' +import { SuccessView } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/components/chat-deploy/components/success-view' +import { useChatDeployment } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/components/chat-deploy/hooks/use-chat-deployment' +import { useChatForm } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/components/chat-deploy/hooks/use-chat-form' import { OutputSelect } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/chat/components' -import type { OutputConfig } from '@/stores/panel/chat/types' const logger = createLogger('ChatDeploy') interface ChatDeployProps { workflowId: string - onClose: () => void deploymentInfo: { apiKey: string } | null onChatExistsChange?: (exists: boolean) => void + chatSubmitting: boolean + setChatSubmitting: (submitting: boolean) => void + onValidationChange?: (isValid: boolean) => void + onPreDeployWorkflow?: () => Promise showDeleteConfirmation?: boolean setShowDeleteConfirmation?: (show: boolean) => void onDeploymentComplete?: () => void } -type AuthType = 'public' | 'password' | 'email' - -const getDomainSuffix = (() => { - const suffix = `.${getEmailDomain()}` - return () => suffix -})() - -const chatSchema = z.object({ - workflowId: z.string().min(1, 'Workflow ID is required'), - subdomain: z - .string() - .min(1, 'Subdomain is required') - .regex(/^[a-z0-9-]+$/, 'Subdomain can only contain lowercase letters, numbers, and hyphens'), - title: z.string().min(1, 'Title is required'), - description: z.string().optional(), - customizations: z.object({ - primaryColor: z.string(), - welcomeMessage: z.string(), - imageUrl: z.string().optional(), - }), - authType: z.enum(['public', 'password', 'email']), - password: z.string().optional(), - allowedEmails: z.array(z.string()).optional(), - outputBlockIds: z.array(z.string()).optional(), - outputPaths: z.array(z.string()).optional(), -}) +interface ExistingChat { + id: string + subdomain: string + title: string + description: string + authType: 'public' | 'password' | 'email' + allowedEmails: string[] + outputConfigs: Array<{ blockId: string; path: string }> + customizations?: { + welcomeMessage?: string + } + isActive: boolean +} export function ChatDeploy({ workflowId, - onClose, deploymentInfo, onChatExistsChange, + chatSubmitting, + setChatSubmitting, + onValidationChange, + onPreDeployWorkflow, showDeleteConfirmation: externalShowDeleteConfirmation, setShowDeleteConfirmation: externalSetShowDeleteConfirmation, onDeploymentComplete, }: ChatDeployProps) { - // Form state - const [subdomain, setSubdomain] = useState('') - const [title, setTitle] = useState('') - const [description, setDescription] = useState('') - const [isDeploying, setIsDeploying] = useState(false) - const [subdomainError, setSubdomainError] = useState('') - const [deployedChatUrl, setDeployedChatUrl] = useState(null) - const [errorMessage, setErrorMessage] = useState(null) - const [isCheckingSubdomain, setIsCheckingSubdomain] = useState(false) - const [subdomainAvailable, setSubdomainAvailable] = useState(null) - const subdomainCheckTimeoutRef = useRef(null) - - // Authentication options - const [authType, setAuthType] = useState('public') - const [password, setPassword] = useState('') - const [showPassword, setShowPassword] = useState(false) - const [emails, setEmails] = useState([]) - const [newEmail, setNewEmail] = useState('') - const [emailError, setEmailError] = useState('') - const [copySuccess, setCopySuccess] = useState(false) - - // Existing chat state - const [existingChat, setExistingChat] = useState(null) - const [isDeleting, setIsDeleting] = useState(false) const [isLoading, setIsLoading] = useState(false) - const [_dataFetched, setDataFetched] = useState(false) - - // Track original values to detect changes - const [originalValues, setOriginalValues] = useState<{ - subdomain: string - title: string - description: string - authType: AuthType - emails: string[] - selectedOutputIds: string[] - } | null>(null) - - const [showEditConfirmation, setShowEditConfirmation] = useState(false) + const [existingChat, setExistingChat] = useState(null) + const [imageUrl, setImageUrl] = useState(null) + const [imageUploadError, setImageUploadError] = useState(null) + const [isDeleting, setIsDeleting] = useState(false) + const [isImageUploading, setIsImageUploading] = useState(false) const [internalShowDeleteConfirmation, setInternalShowDeleteConfirmation] = useState(false) - - // Output block selection - const [selectedOutputBlocks, setSelectedOutputBlocks] = useState([]) - - // Track manual submission state - const [chatSubmitting, setChatSubmitting] = useState(false) - - // Set up a ref for the form element - const formRef = useRef(null) + const [showSuccessView, setShowSuccessView] = useState(false) // Use external state for delete confirmation if provided const showDeleteConfirmation = @@ -145,247 +91,121 @@ export function ChatDeploy({ const setShowDeleteConfirmation = externalSetShowDeleteConfirmation || setInternalShowDeleteConfirmation - // Welcome message state - const [welcomeMessage, setWelcomeMessage] = useState('Hi there! How can I help you today?') - - // Image URL state - const [imageUrl, setImageUrl] = useState(null) + const { formData, errors, updateField, setError, validateForm, setFormData } = useChatForm() + const { deployedUrl, deployChat } = useChatDeployment() + const formRef = useRef(null) + const [isSubdomainValid, setIsSubdomainValid] = useState(false) + const isFormValid = + isSubdomainValid && + Boolean(formData.title.trim()) && + formData.selectedOutputBlocks.length > 0 && + (formData.authType !== 'password' || + Boolean(formData.password.trim()) || + Boolean(existingChat)) && + (formData.authType !== 'email' || formData.emails.length > 0) - // Expose a method to handle external submission requests useEffect(() => { - // This will run when the component mounts - // Ensure hidden input for API deployment is set up - if (formRef.current) { - let deployApiInput = formRef.current.querySelector('#deployApiEnabled') as HTMLInputElement - if (!deployApiInput) { - deployApiInput = document.createElement('input') - deployApiInput.type = 'hidden' - deployApiInput.id = 'deployApiEnabled' - deployApiInput.name = 'deployApiEnabled' - deployApiInput.value = 'true' - formRef.current.appendChild(deployApiInput) - } - } + onValidationChange?.(isFormValid) + }, [isFormValid, onValidationChange]) - // Clean up any loading states - return () => { - setIsDeploying(false) - setChatSubmitting(false) - } - }, []) - - // Fetch existing chat data when component mounts useEffect(() => { if (workflowId) { - setIsLoading(true) - setDataFetched(false) fetchExistingChat() } }, [workflowId]) - // Fetch existing chat data for this workflow const fetchExistingChat = async () => { try { + setIsLoading(true) const response = await fetch(`/api/workflows/${workflowId}/chat/status`) if (response.ok) { const data = await response.json() if (data.isDeployed && data.deployment) { - // Get detailed chat info const detailResponse = await fetch(`/api/chat/edit/${data.deployment.id}`) if (detailResponse.ok) { const chatDetail = await detailResponse.json() setExistingChat(chatDetail) - // Notify parent component that a chat exists - if (onChatExistsChange) { - onChatExistsChange(true) - } - - // Populate form with existing data - setSubdomain(chatDetail.subdomain || '') - setTitle(chatDetail.title || '') - setDescription(chatDetail.description || '') - setAuthType(chatDetail.authType || 'public') - - // Store original values for change detection - setOriginalValues({ + setFormData({ subdomain: chatDetail.subdomain || '', title: chatDetail.title || '', description: chatDetail.description || '', authType: chatDetail.authType || 'public', + password: '', emails: Array.isArray(chatDetail.allowedEmails) ? [...chatDetail.allowedEmails] : [], - selectedOutputIds: Array.isArray(chatDetail.outputConfigs) + welcomeMessage: + chatDetail.customizations?.welcomeMessage || 'Hi there! How can I help you today?', + selectedOutputBlocks: Array.isArray(chatDetail.outputConfigs) ? chatDetail.outputConfigs.map( - (config: OutputConfig) => `${config.blockId}_${config.path}` + (config: { blockId: string; path: string }) => + `${config.blockId}_${config.path}` ) : [], }) - // Set emails if using email auth - if (chatDetail.authType === 'email' && Array.isArray(chatDetail.allowedEmails)) { - setEmails(chatDetail.allowedEmails) - } - - // For security, we don't populate password - user will need to enter a new one if changing it - - // Inside the fetchExistingChat function - update how we load output configs - if (chatDetail.outputConfigs) { - const configs = Array.isArray(chatDetail.outputConfigs) - ? (chatDetail.outputConfigs as OutputConfig[]) - : [] - const combinedOutputIds = configs.map((config) => `${config.blockId}_${config.path}`) - setSelectedOutputBlocks(combinedOutputIds) - } - - // Set welcome message if it exists - if (chatDetail.customizations?.welcomeMessage) { - setWelcomeMessage(chatDetail.customizations.welcomeMessage) - } - // Set image URL if it exists if (chatDetail.customizations?.imageUrl) { setImageUrl(chatDetail.customizations.imageUrl) } - } else { - logger.error('Failed to fetch chat details') + setImageUploadError(null) + + onChatExistsChange?.(true) } } else { setExistingChat(null) - setOriginalValues(null) - - // Notify parent component that no chat exists - if (onChatExistsChange) { - onChatExistsChange(false) - } + setImageUrl(null) + setImageUploadError(null) + onChatExistsChange?.(false) } } } catch (error) { logger.error('Error fetching chat status:', error) } finally { setIsLoading(false) - setDataFetched(true) } } - // Validate subdomain format on input change and check availability - const handleSubdomainChange = (value: string) => { - const lowercaseValue = value.toLowerCase() - setSubdomain(lowercaseValue) - setSubdomainAvailable(null) - - // Clear any existing timeout - if (subdomainCheckTimeoutRef.current) { - clearTimeout(subdomainCheckTimeoutRef.current) - } - - // Validate subdomain format - if (lowercaseValue && !/^[a-z0-9-]+$/.test(lowercaseValue)) { - setSubdomainError('Subdomain can only contain lowercase letters, numbers, and hyphens') - // Reset deploying states when validation errors occur - setIsDeploying(false) - setChatSubmitting(false) - return - } - setSubdomainError('') - - // Skip check if empty or same as original (for updates) - if (!lowercaseValue || (originalValues && lowercaseValue === originalValues.subdomain)) { - return - } - - // Debounce check to avoid unnecessary API calls - subdomainCheckTimeoutRef.current = setTimeout(() => { - checkSubdomainAvailability(lowercaseValue) - }, 500) - } + const handleSubmit = async (e?: React.FormEvent) => { + if (e) e.preventDefault() - // Check if subdomain is available - const checkSubdomainAvailability = async (domain: string) => { - if (!domain) return + if (chatSubmitting) return - setIsCheckingSubdomain(true) + setChatSubmitting(true) try { - const response = await fetch( - `/api/chat/subdomains/validate?subdomain=${encodeURIComponent(domain)}` - ) - const data = await response.json() + await onPreDeployWorkflow?.() - // Only update if this is still the current subdomain - if (domain === subdomain) { - if (response.ok) { - setSubdomainAvailable(data.available) - if (!data.available) { - setSubdomainError('This subdomain is already in use') - // Reset deploying states when subdomain is unavailable - setIsDeploying(false) - setChatSubmitting(false) - } else { - setSubdomainError('') - } - } else { - setSubdomainError('Error checking subdomain availability') - // Reset deploying states on API error - setIsDeploying(false) - setChatSubmitting(false) - } + if (!validateForm()) { + setChatSubmitting(false) + return } - } catch (error) { - logger.error('Error checking subdomain availability:', error) - setSubdomainError('Error checking subdomain availability') - // Reset deploying states on error - setIsDeploying(false) - setChatSubmitting(false) - } finally { - setIsCheckingSubdomain(false) - } - } - - // Validate and add email - const handleAddEmail = () => { - // Basic email validation - if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(newEmail) && !newEmail.startsWith('@')) { - setEmailError('Please enter a valid email or domain (e.g., user@example.com or @example.com)') - return - } - // Add email if it's not already in the list - if (!emails.includes(newEmail)) { - setEmails([...emails, newEmail]) - setNewEmail('') - setEmailError('') - } else { - setEmailError('This email or domain is already in the list') - } - } + if (!isSubdomainValid && formData.subdomain !== existingChat?.subdomain) { + setError('subdomain', 'Please wait for subdomain validation to complete') + setChatSubmitting(false) + return + } - // Remove email from the list - const handleRemoveEmail = (email: string) => { - setEmails(emails.filter((e) => e !== email)) - } + await deployChat(workflowId, formData, deploymentInfo, existingChat?.id, imageUrl) - // Password generation and copy functionality - const generatePassword = () => { - const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_-+=' - let result = '' - const length = 24 + onChatExistsChange?.(true) + setShowSuccessView(true) - for (let i = 0; i < length; i++) { - result += chars.charAt(Math.floor(Math.random() * chars.length)) + // Fetch the updated chat data immediately after deployment + // This ensures existingChat is available when switching back to edit mode + await fetchExistingChat() + } catch (error: any) { + if (error.message?.includes('subdomain')) { + setError('subdomain', error.message) + } else { + setError('general', error.message) + } + } finally { + setChatSubmitting(false) } - - setPassword(result) - } - - const copyToClipboard = (text: string) => { - navigator.clipboard.writeText(text) - setCopySuccess(true) - setTimeout(() => { - setCopySuccess(false) - }, 2000) } const handleDelete = async () => { @@ -403,804 +223,161 @@ export function ChatDeploy({ throw new Error(error.error || 'Failed to delete chat') } - // Close modal after successful deletion - onClose() + // Update state + setExistingChat(null) + setImageUrl(null) + setImageUploadError(null) + onChatExistsChange?.(false) + + // Notify parent of successful deletion + onDeploymentComplete?.() } catch (error: any) { logger.error('Failed to delete chat:', error) - setErrorMessage(error.message || 'An unexpected error occurred while deleting') + setError('general', error.message || 'An unexpected error occurred while deleting') } finally { setIsDeleting(false) setShowDeleteConfirmation(false) } } - // Deploy or update chat - const handleSubmit = async (e?: FormEvent) => { - if (e) e.preventDefault() - - // If already submitting, don't process again - if (chatSubmitting) return - - setChatSubmitting(true) - setErrorMessage(null) - - // Log form state to help debug - logger.info('Form submission triggered with values:', { - subdomain, - title, - authType, - hasOutputBlockSelection: !!selectedOutputBlocks.length, - }) - - // Basic validation - if (!workflowId || !subdomain.trim() || !title.trim()) { - logger.error('Missing required fields', { workflowId, subdomain, title }) - setChatSubmitting(false) - setErrorMessage('Please fill out all required fields') - return - } - - // Check subdomain availability before submission if it's different from original - if ( - (!existingChat || subdomain !== existingChat.subdomain) && - (!originalValues || subdomain !== originalValues.subdomain) - ) { - setIsCheckingSubdomain(true) - setSubdomainError('') - - try { - const response = await fetch( - `/api/chat/subdomains/validate?subdomain=${encodeURIComponent(subdomain)}` - ) - const data = await response.json() - - if (!response.ok || !data.available) { - const errorMsg = data.error || 'This subdomain is already in use' - setSubdomainError(errorMsg) - setChatSubmitting(false) - setIsCheckingSubdomain(false) - logger.warn('Subdomain validation failed:', errorMsg) - return - } - - setSubdomainError('') - } catch (error) { - logger.error('Error checking subdomain availability:', error) - setSubdomainError('Error checking subdomain availability') - setChatSubmitting(false) - setIsCheckingSubdomain(false) - return - } - setIsCheckingSubdomain(false) - } - - if (subdomainError) { - logger.warn('Blocking submission due to subdomain error:', subdomainError) - setChatSubmitting(false) - return - } - - // Verify output selection if it's set - if (selectedOutputBlocks.length === 0) { - logger.error('No output blocks selected') - setErrorMessage('Please select at least one output block') - setChatSubmitting(false) - return - } - - // Validate authentication options - if (authType === 'password' && !password.trim() && !existingChat) { - setErrorMessage('Password is required when using password protection') - setChatSubmitting(false) - return - } - - if (authType === 'email' && emails.length === 0) { - setErrorMessage('At least one email or domain is required when using email access control') - setChatSubmitting(false) - return - } - - // If editing an existing chat, check if we should show confirmation - if (existingChat?.isActive) { - const majorChanges = - subdomain !== existingChat.subdomain || - authType !== existingChat.authType || - (authType === 'email' && - JSON.stringify(emails) !== JSON.stringify(existingChat.allowedEmails)) - - if (majorChanges) { - setShowEditConfirmation(true) - setChatSubmitting(false) - return - } - } - - // Proceed with create/update - await deployOrUpdateChat() - } - - // Actual deployment/update logic - const deployOrUpdateChat = async () => { - setErrorMessage(null) - - try { - // Create request payload - const payload: any = { - workflowId, - subdomain: subdomain.trim(), - title: title.trim(), - description: description.trim(), - customizations: { - primaryColor: '#802FFF', - welcomeMessage: welcomeMessage.trim(), - ...(imageUrl && { imageUrl }), - }, - authType: authType, - } - - // Always include auth specific fields regardless of authType - // This ensures they're always properly handled - if (authType === 'password') { - // For password auth, only send the password if: - // 1. It's a new chat, or - // 2. Creating a new password for an existing chat, or - // 3. Changing from another auth type to password - if (password) { - payload.password = password - } else if (existingChat && existingChat.authType !== 'password') { - // If changing to password auth but no password provided for an existing chat, - // this is an error - server will reject it - setErrorMessage('Password is required when using password protection') - setChatSubmitting(false) - return // Stop the submission - } - - payload.allowedEmails = [] // Clear emails when using password auth - } else if (authType === 'email') { - payload.allowedEmails = emails - } else if (authType === 'public') { - // Explicitly set empty values for public access - payload.allowedEmails = [] - } - - // Add output block configuration if selected - if (selectedOutputBlocks && selectedOutputBlocks.length > 0) { - const outputConfigs = selectedOutputBlocks - .map((outputId) => { - const firstUnderscoreIndex = outputId.indexOf('_') - // Only process IDs that have the correct blockId_path format - if (firstUnderscoreIndex !== -1) { - const blockId = outputId.substring(0, firstUnderscoreIndex) - const path = outputId.substring(firstUnderscoreIndex + 1) - - // Additional validation to ensure both parts are non-empty - if (blockId && path) { - return { blockId, path } as OutputConfig - } - logger.warn(`Invalid output format: ${outputId}, missing blockId or path`) - return null - } - logger.warn( - `Invalid output ID format: ${outputId}, missing required format blockId_path` - ) - return null - }) - .filter(Boolean) as OutputConfig[] // Remove any null values - - // Only include output configurations if we have valid ones - if (outputConfigs.length > 0) { - payload.outputConfigs = outputConfigs - - logger.info('Added output configuration to payload:', { - outputConfigsCount: outputConfigs.length, - outputConfigs: outputConfigs, - }) - } else { - logger.warn('No valid output configurations found in selection') - payload.outputConfigs = [] - } - } else { - // No output blocks selected - explicitly set to empty array - payload.outputConfigs = [] - } - - // Pass the API key from workflow deployment - if (deploymentInfo?.apiKey) { - payload.apiKey = deploymentInfo.apiKey - } - - // For existing chat updates, ensure API gets redeployed too - if (existingChat?.id) { - // First ensure the API deployment is up-to-date - try { - // Make a direct call to redeploy the API - const redeployResponse = await fetch(`/api/workflows/${workflowId}/deploy`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - deployApiEnabled: true, - deployChatEnabled: false, - }), - }) - - if (!redeployResponse.ok) { - logger.warn('API redeployment failed, continuing with chat update') - } else { - logger.info('API successfully redeployed alongside chat update') - } - } catch (error) { - logger.warn('Error redeploying API, continuing with chat update:', error) - } - } else { - // For new chat deployments, set the flag for API deployment - payload.deployApiEnabled = true - } - - // Log the final payload (minus sensitive data) for debugging - logger.info('Submitting chat deployment with values:', { - workflowId: payload.workflowId, - subdomain: payload.subdomain, - title: payload.title, - authType: payload.authType, - hasPassword: !!payload.password, - emailCount: payload.allowedEmails?.length || 0, - hasOutputConfig: !!payload.outputConfigs.length, - deployApiEnabled: payload.deployApiEnabled, - }) - - // Make API request - different endpoints for create vs update - let endpoint = '/api/chat' - let method = 'POST' - - // If updating existing chat, use the edit/ID endpoint with PATCH method - if (existingChat?.id) { - endpoint = `/api/chat/edit/${existingChat.id}` - method = 'PATCH' - // Ensure deployApiEnabled is included in updates too - payload.deployApiEnabled = true - } - - // Validate with Zod - try { - chatSchema.parse(payload) - } catch (validationError: any) { - if (validationError instanceof z.ZodError) { - const errorMessage = validationError.errors[0]?.message || 'Invalid form data' - setErrorMessage(errorMessage) - setChatSubmitting(false) - return - } - } - - const response = await fetch(endpoint, { - method, - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(payload), - }) - - const result = await response.json() - - if (!response.ok) { - if (result.error === 'Subdomain already in use') { - setSubdomainError(result.error) - setChatSubmitting(false) - return - } - throw new Error(result.error || `Failed to ${existingChat ? 'update' : 'deploy'} chat`) - } - - const { chatUrl } = result - - if (chatUrl) { - logger.info(`Chat ${existingChat ? 'updated' : 'deployed'} successfully:`, chatUrl) - setDeployedChatUrl(chatUrl) - - if (onDeploymentComplete) { - onDeploymentComplete() - } - - if (onChatExistsChange) { - onChatExistsChange(true) - } - } else { - throw new Error('Response missing chatUrl') - } - } catch (error: any) { - logger.error(`Failed to ${existingChat ? 'update' : 'deploy'} chat:`, error) - - const errorMessage = error.message || 'An unexpected error occurred' - if (errorMessage.includes('Subdomain already in use') || errorMessage.includes('subdomain')) { - setSubdomainError(errorMessage) - } else { - setErrorMessage(errorMessage) - } - - logger.error(`Failed to deploy chat: ${error.message}`, workflowId) - } finally { - setChatSubmitting(false) - setShowEditConfirmation(false) - } - } - if (isLoading) { - return ( -
- {/* Subdomain section */} -
- - -
- - {/* Title section */} -
- - -
- - {/* Description section */} -
- - -
- - {/* Output configuration section */} -
- - -
- - {/* Access control section */} -
- -
- - - -
- -
- - {/* Submit button */} - -
- ) + return } - if (deployedChatUrl) { - const url = new URL(deployedChatUrl) - const hostname = url.hostname - const isDevelopmentUrl = hostname.includes('localhost') - - let domainSuffix - if (isDevelopmentUrl) { - const baseDomain = getBaseDomain() - const baseHost = baseDomain.split(':')[0] - const port = url.port || (baseDomain.includes(':') ? baseDomain.split(':')[1] : '3000') - domainSuffix = `.${baseHost}:${port}` - } else { - domainSuffix = `.${getEmailDomain()}` - } - - const baseDomainForSplit = getEmailDomain() - const subdomainPart = isDevelopmentUrl - ? hostname.split('.')[0] - : hostname.split(`.${baseDomainForSplit}`)[0] - - // Success view - simplified with no buttons + if (deployedUrl && showSuccessView) { return ( -
-
- -
- - {subdomainPart} - -
- {domainSuffix} -
-
-

- Your chat is now live at{' '} - - this URL - -

-
-
- ) - } - - if (errorMessage) { - return ( -
-
-
Chat Deployment Error
-
{errorMessage}
+ <> +
+ setShowDeleteConfirmation(true)} + onUpdate={() => setShowSuccessView(false)} + />
- {/* Add button to try again */} -
- -
-
+ {/* Delete Confirmation Dialog */} + + + + Delete Chat? + + This will permanently delete your chat deployment at{' '} + + {existingChat?.subdomain}.{getEmailDomain()} + + . + + All users will lose access immediately, and this action cannot be undone. + + + + + Cancel + + {isDeleting ? ( + + + Deleting... + + ) : ( + 'Delete' + )} + + + + + ) } - // Form view return ( <>
{ - e.preventDefault() // Prevent default form submission - handleSubmit(e) // Call our submit handler directly - }} - className='chat-deploy-form -mx-1 space-y-4 overflow-y-auto px-1' + onSubmit={handleSubmit} + className='-mx-1 space-y-4 overflow-y-auto px-1' > -
- {errorMessage && ( - - - {errorMessage} - - )} - -
-
-
- -
- handleSubdomainChange(e.target.value)} - required - className={cn( - 'rounded-r-none border-r-0 focus-visible:ring-0 focus-visible:ring-offset-0', - subdomainAvailable === true && - 'border-green-500 focus-visible:border-green-500', - subdomainAvailable === false && - 'border-destructive focus-visible:border-destructive' - )} - disabled={isDeploying} - /> -
- {getDomainSuffix()} -
- {!isCheckingSubdomain && subdomainAvailable === true && subdomain && ( -
- -
- )} -
- {subdomainError && ( -

{subdomainError}

- )} - {!subdomainError && subdomainAvailable === true && subdomain && ( -

Subdomain is available

- )} -
- -
- - setTitle(e.target.value)} - required - disabled={isDeploying} - /> -
-
- -
- -